From 04503bc70c1762b657392f21a8cb35dca9ac1589 Mon Sep 17 00:00:00 2001 From: uclaros Date: Fri, 16 Aug 2024 08:55:15 +0300 Subject: [PATCH] wip --- src/core/CMakeLists.txt | 2 + src/core/stac/qgsstaccontroller.cpp | 32 +++++- src/core/stac/qgsstaccontroller.h | 3 +- src/core/stac/qgsstacdataitems.cpp | 119 +++++++++++++++++++-- src/core/stac/qgsstacdataitems.h | 25 ++++- src/core/stac/qgsstacfeaturecollection.cpp | 71 ++++++++++++ src/core/stac/qgsstacfeaturecollection.h | 49 +++++++++ src/core/stac/qgsstacobject.cpp | 32 ++++++ src/core/stac/qgsstacobject.h | 5 + src/core/stac/qgsstacparser.cpp | 94 ++++++++++++---- src/core/stac/qgsstacparser.h | 8 ++ 11 files changed, 407 insertions(+), 33 deletions(-) create mode 100644 src/core/stac/qgsstacfeaturecollection.cpp create mode 100644 src/core/stac/qgsstacfeaturecollection.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index da575e6369ea..c5a0b38228c3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -359,6 +359,7 @@ set(QGIS_CORE_SRCS stac/qgsstaccontroller.cpp stac/qgsstacdataitems.cpp stac/qgsstacextent.cpp + stac/qgsstacfeaturecollection.cpp stac/qgsstacitem.cpp stac/qgsstacobject.cpp stac/qgsstacparser.cpp @@ -1933,6 +1934,7 @@ set(QGIS_CORE_HDRS stac/qgsstaccontroller.h stac/qgsstacdataitems.h stac/qgsstacextent.h + stac/qgsstacfeaturecollection.h stac/qgsstacitem.h stac/qgsstacobject.h stac/qgsstacparser.h diff --git a/src/core/stac/qgsstaccontroller.cpp b/src/core/stac/qgsstaccontroller.cpp index c6c0c0b53317..30e06aa42dbe 100644 --- a/src/core/stac/qgsstaccontroller.cpp +++ b/src/core/stac/qgsstaccontroller.cpp @@ -24,7 +24,7 @@ QgsStacController::QgsStacController() { } -QgsStacObject *QgsStacController::fetch( QUrl url ) +QgsStacObject *QgsStacController::fetchObject( QUrl url ) { QNetworkRequest req( url ); QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance(); @@ -34,7 +34,7 @@ QgsStacObject *QgsStacController::fetch( QUrl url ) if ( content.error() != QNetworkReply::NoError ) return nullptr; - auto data = content.content(); + const QByteArray data = content.content(); QgsStacParser parser; parser.setData( data ); @@ -47,11 +47,39 @@ QgsStacObject *QgsStacController::fetch( QUrl url ) case QgsStacObject::Type::Item: return new QgsStacItem( parser.item() ); case QgsStacObject::Type::Unknown: + case QgsStacObject::Type::FeatureCollection: return nullptr; } Q_UNREACHABLE(); } +QgsStacFeatureCollection *QgsStacController::fetchItems( QUrl url ) +{ + QNetworkRequest req( url ); + QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance(); + + QgsNetworkReplyContent content = nam->blockingGet( req ); + + if ( content.error() != QNetworkReply::NoError ) + return nullptr; + + const QByteArray data = content.content(); + + QgsStacParser parser; + parser.setData( data ); + switch ( parser.type() ) + { + case QgsStacObject::Type::Catalog: + case QgsStacObject::Type::Collection: + case QgsStacObject::Type::Item: + case QgsStacObject::Type::Unknown: + return nullptr; + case QgsStacObject::Type::FeatureCollection: + return new QgsStacFeatureCollection( parser.featureCollection() ); + } + Q_UNREACHABLE(); +} + QgsStacCatalog QgsStacController::openLocalCatalog( const QString &fileName ) const { QFile file( fileName ); diff --git a/src/core/stac/qgsstaccontroller.h b/src/core/stac/qgsstaccontroller.h index 588af7e45d83..d0c42f03eab1 100644 --- a/src/core/stac/qgsstaccontroller.h +++ b/src/core/stac/qgsstaccontroller.h @@ -40,7 +40,8 @@ class CORE_EXPORT QgsStacController : public QObject QgsStacItem openLocalItem( const QString &fileName ) const; - QgsStacObject *fetch( QUrl url ); + QgsStacObject *fetchObject( QUrl url ); + QgsStacFeatureCollection *fetchItems( QUrl url ); signals: diff --git a/src/core/stac/qgsstacdataitems.cpp b/src/core/stac/qgsstacdataitems.cpp index 9d10f7efa438..031ebcfeb59c 100644 --- a/src/core/stac/qgsstacdataitems.cpp +++ b/src/core/stac/qgsstacdataitems.cpp @@ -18,24 +18,67 @@ #include "stac/qgsstaccontroller.h" + + + // -// QgsStacAssetItem +// QgsStacFetchMoreItem // -QgsStacAssetItem::QgsStacAssetItem( QgsDataItem *parent, QString name, QString path, QString uri ) +QgsStacFetchMoreItem::QgsStacFetchMoreItem( QgsDataItem *parent, QString name, QString path, QString uri ) : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, path, uri ) { mIconName = QStringLiteral( "mIconFieldArrayString.svg" ); mToolTip = QStringLiteral( "%1\n%2" ).arg( name, uri ); } +bool QgsStacFetchMoreItem::handleDoubleClick() +{ + QgsStacCatalogItem *c = dynamic_cast( mParent ); + if ( !c ) + return false; + + + c->fetchMoreChildren(); + + return true; +} + +// +// QgsStacAssetItem +// + +QgsStacAssetItem::QgsStacAssetItem( QgsDataItem *parent, QgsStacAsset asset ) + : QgsDataItem( Qgis::BrowserItemType::Custom, parent, asset.title, asset.title ) + , mAsset( asset ) +{ + mIconName = QStringLiteral( "mIconFieldArrayString.svg" ); + mToolTip = QStringLiteral( "%1
%2
%3
%4" ).arg( asset.title, asset.href, asset.mediaType, asset.description ); + mState = Qgis::BrowserItemState::Populated; +} + +bool QgsStacAssetItem::hasDragEnabled() const +{ + return mAsset.mediaType == QLatin1String( "image/tiff; application=geotiff; profile=cloud-optimized" ); +} + +QgsMimeDataUtils::UriList QgsStacAssetItem::mimeUris() const +{ + QgsMimeDataUtils::Uri uri; + uri.layerType = QStringLiteral( "raster" ); + uri.providerKey = QStringLiteral( "gdal" ); + uri.uri = mAsset.href; + uri.name = mAsset.title; + return { uri, uri }; +} + // // QgsStacItemItem // QgsStacItemItem::QgsStacItemItem( QgsDataItem *parent, QString name, QString path, QString uri ) - : QgsDataCollectionItem( parent, name, path, QStringLiteral( "stac" ) ) + : QgsDataCollectionItem( parent, name, path, QStringLiteral( "special:Stac" ) ) { mIconName = QStringLiteral( "mActionPropertiesWidget.svg" ); mToolTip = QStringLiteral( "%1\n%2" ).arg( name, uri ); @@ -53,7 +96,7 @@ QVector QgsStacItemItem::createChildren() // QgsStacCatalogItem::QgsStacCatalogItem( QgsDataItem *parent, QString name, QString path, QString uri ) - : QgsDataCollectionItem( parent, name, path, QStringLiteral( "stac" ) ) + : QgsDataCollectionItem( parent, name, path, QStringLiteral( "special:Stac" ) ) , mUri( uri ) { mIconName = QStringLiteral( "mIconFolder.svg" ); @@ -65,7 +108,7 @@ QVector QgsStacCatalogItem::createChildren() { auto d = QgsStacConnection::decodedUri( mUri ); QgsStacController c; - auto i = c.fetch( d.url ); + auto i = c.fetchObject( d.url ); if ( !i || i->type() != QgsStacObject::Type::Catalog ) return { new QgsErrorItem( this, i ? QStringLiteral( "This is not a catalog!" ) : QStringLiteral( "Error fetching..." ), QStringLiteral( "stac" ) ) }; @@ -100,6 +143,38 @@ QVector QgsStacCatalogItem::createChildren() else if ( link.relation == QLatin1String( "items" ) ) { // stac api items (ogcapi features) + const QgsStacFeatureCollection *fc = c.fetchItems( d.url ); // todo: pass Data or encodedUri? + if ( fc ) + { + for ( const auto &item : fc->items() ) + { + if ( !item.isValid() ) + continue; + + d.url = item.url(); + const QString name = item.properties().value( QStringLiteral( "title" ), item.id() ).toString(); + QgsStacItemItem *i = new QgsStacItemItem( this, name, d.url, QgsStacConnection::encodedUri( d ) ); + QString toolTip = QStringLiteral( "%1\n%2\n%3" ).arg( name, item.id(), d.url ); + + for ( const auto &asset : item.assets() ) + { + if ( asset.roles.contains( QLatin1String( "data" ) ) ) + { + QgsStacAssetItem *a = new QgsStacAssetItem( i, asset ); + i->addChild( a ); + } + if ( asset.roles.contains( QLatin1String( "thumbnail" ) ) ) + { + toolTip.prepend( QStringLiteral( "
" ).arg( asset.href ) ); + } + } + + i->setToolTip( toolTip ); + i->setState( Qgis::BrowserItemState::Populated ); + contents.append( i ); + } + } + mFetchMoreUrl = fc->nextUrl(); } else if ( link.mediaType == QLatin1String( "application/json" ) ) { @@ -110,9 +185,41 @@ QVector QgsStacCatalogItem::createChildren() // should handle other links? } } + + if ( !mFetchMoreUrl.isEmpty() ) + { + mFetchMoreItem = new QgsStacFetchMoreItem( this, QStringLiteral( "Fetch more items..." ), QString(), QString() ); + contents.append( mFetchMoreItem ); + } return contents; } +void QgsStacCatalogItem::fetchMoreChildren() +{ + QgsStacController c; + const QgsStacFeatureCollection *fc = c.fetchItems( mFetchMoreUrl ); + if ( fc ) + { + auto d = QgsStacConnection::decodedUri( mUri ); + for ( const auto &item : fc->items() ) + { + if ( !item.isValid() ) + continue; + + d.url = item.url(); + QgsStacItemItem *i = new QgsStacItemItem( this, item.id(), d.url, QgsStacConnection::encodedUri( d ) ); + i->setToolTip( QStringLiteral( "%1\n%2" ).arg( item.id(), d.url ) ); + addChildItem( i, true ); + } + } + mFetchMoreUrl = fc->nextUrl(); + if ( mFetchMoreUrl.isEmpty() && mFetchMoreItem ) + { + removeChildItem( mFetchMoreItem ); + mFetchMoreItem = nullptr; + } +} + // // QgsStacCollectionItem @@ -130,7 +237,7 @@ QgsStacCollectionItem::QgsStacCollectionItem( QgsDataItem *parent, QString name, // QgsStacRootItem::QgsStacRootItem( QgsDataItem *parent, const QString &name, const QString &path ) - : QgsConnectionsRootItem( parent, name, path, QStringLiteral( "stac" ) ) + : QgsConnectionsRootItem( parent, name, path, QStringLiteral( "special:Stac" ) ) // todo: do we actually need Provider? { mCapabilities |= Qgis::BrowserItemCapability::Fast; mIconName = QStringLiteral( "mIconStac.svg" ); diff --git a/src/core/stac/qgsstacdataitems.h b/src/core/stac/qgsstacdataitems.h index fde0fde6de1d..9ca2284f12e7 100644 --- a/src/core/stac/qgsstacdataitems.h +++ b/src/core/stac/qgsstacdataitems.h @@ -19,13 +19,33 @@ #include "qgis_core.h" #include "qgsdataitemprovider.h" #include "qgsconnectionsitem.h" +#include "qgsstacobject.h" + + +// +// QgsStacFetchMoreItem +// + +class QgsStacFetchMoreItem : public QgsDataItem +{ + Q_OBJECT + public: + QgsStacFetchMoreItem( QgsDataItem *parent, QString name, QString path, QString uri ); + virtual bool handleDoubleClick() override; +}; class QgsStacAssetItem : public QgsDataItem { Q_OBJECT public: - QgsStacAssetItem( QgsDataItem *parent, QString name, QString path, QString uri ); + QgsStacAssetItem( QgsDataItem *parent, QgsStacAsset asset ); + + virtual bool hasDragEnabled() const override; + virtual QgsMimeDataUtils::UriList mimeUris() const override; + + private: + QgsStacAsset mAsset; }; @@ -47,10 +67,13 @@ class QgsStacCatalogItem : public QgsDataCollectionItem QVector createChildren() override; // bool equal( const QgsDataItem *other ) override; + void fetchMoreChildren(); protected: //! The URI QString mUri; + QString mFetchMoreUrl; + QgsDataItem *mFetchMoreItem = nullptr; }; diff --git a/src/core/stac/qgsstacfeaturecollection.cpp b/src/core/stac/qgsstacfeaturecollection.cpp new file mode 100644 index 000000000000..96cb18c607d6 --- /dev/null +++ b/src/core/stac/qgsstacfeaturecollection.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + qgsstacfeaturecollection.cpp + --------------------- + begin : August 2024 + copyright : (C) 2024 by Stefanos Natsis + email : uclaros 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 "qgsstacfeaturecollection.h" + +QgsStacFeatureCollection::QgsStacFeatureCollection( const QVector< QgsStacItem > items, const QVector< QgsStacLink > links ) + : mValid( true ) + , mItems( items ) + , mLinks( links ) +{ + for ( const QgsStacLink &link : mLinks ) + { + if ( link.relation == QLatin1String( "self" ) ) + mUrl = link.href; + else if ( link.relation == QLatin1String( "root" ) ) + mRootUrl = link.href; + else if ( link.relation == QLatin1String( "parent" ) ) + mParentUrl = link.href; + else if ( link.relation == QLatin1String( "collection" ) ) + mCollectionUrl = link.href; + else if ( link.relation == QLatin1String( "next" ) ) + mNextUrl = link.href; + } +} + +QVector< QgsStacItem > QgsStacFeatureCollection::items() const +{ + return mItems; +} + +QString QgsStacFeatureCollection::url() const +{ + return mUrl; +} + +QString QgsStacFeatureCollection::rootUrl() const +{ + return mRootUrl; +} + +QString QgsStacFeatureCollection::parentUrl() const +{ + return mParentUrl; +} + +QString QgsStacFeatureCollection::collectionUrl() const +{ + return mCollectionUrl; +} + +QString QgsStacFeatureCollection::nextUrl() const +{ + return mNextUrl; +} + +bool QgsStacFeatureCollection::isValid() const +{ + return mValid; +} diff --git a/src/core/stac/qgsstacfeaturecollection.h b/src/core/stac/qgsstacfeaturecollection.h new file mode 100644 index 000000000000..73508d6046b0 --- /dev/null +++ b/src/core/stac/qgsstacfeaturecollection.h @@ -0,0 +1,49 @@ +/*************************************************************************** + qgsstacfeaturecollection.h + --------------------- + begin : August 2024 + copyright : (C) 2024 by Stefanos Natsis + email : uclaros 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 QGSSTACFEATURECOLLECTION_H +#define QGSSTACFEATURECOLLECTION_H + +#include "qgis_core.h" +#include "qgsstacitem.h" + +class CORE_EXPORT QgsStacFeatureCollection +{ + public: + QgsStacFeatureCollection() = default; + QgsStacFeatureCollection( const QVector< QgsStacItem > items, const QVector< QgsStacLink > links ); + + QVector< QgsStacItem > items() const; + + QString url() const; + QString rootUrl() const; + QString parentUrl() const; + QString collectionUrl() const; + QString nextUrl() const; + + bool isValid() const; + + private: + bool mValid = false; + const QVector< QgsStacItem > mItems; + const QVector< QgsStacLink > mLinks; + QString mUrl; + QString mRootUrl; + QString mParentUrl; + QString mCollectionUrl; + QString mNextUrl; +}; + +#endif // QGSSTACFEATURECOLLECTION_H diff --git a/src/core/stac/qgsstacobject.cpp b/src/core/stac/qgsstacobject.cpp index c5eebd65ea56..c4b6e1af7341 100644 --- a/src/core/stac/qgsstacobject.cpp +++ b/src/core/stac/qgsstacobject.cpp @@ -74,3 +74,35 @@ void QgsStacObject::setLinks( const QVector< QgsStacLink > &links ) { mLinks = links; } + +QString QgsStacObject::url() const +{ + for ( const QgsStacLink &link : mLinks ) + { + if ( link.relation == QLatin1String( "self" ) ) + return link.href; + } + return QString(); +} + +QString QgsStacObject::rootUrl() const +{ + for ( const QgsStacLink &link : mLinks ) + { + if ( link.relation == QLatin1String( "root" ) ) + return link.href; + } + return QString(); +} + +QString QgsStacObject::parentUrl() const +{ + for ( const QgsStacLink &link : mLinks ) + { + if ( link.relation == QLatin1String( "parent" ) ) + return link.href; + } + return QString(); +} + + diff --git a/src/core/stac/qgsstacobject.h b/src/core/stac/qgsstacobject.h index 7098e5e4b15b..7ebfc786dc0f 100644 --- a/src/core/stac/qgsstacobject.h +++ b/src/core/stac/qgsstacobject.h @@ -56,6 +56,7 @@ class CORE_EXPORT QgsStacObject Catalog, Collection, Item, + FeatureCollection, }; QgsStacObject(); @@ -74,6 +75,10 @@ class CORE_EXPORT QgsStacObject QVector< QgsStacLink > links() const; void setLinks( const QVector< QgsStacLink > &links ); + QString url() const; + QString rootUrl() const; + QString parentUrl() const; + protected: bool mValid = false; diff --git a/src/core/stac/qgsstacparser.cpp b/src/core/stac/qgsstacparser.cpp index f732be1f2ac8..f0cc0904d64d 100644 --- a/src/core/stac/qgsstacparser.cpp +++ b/src/core/stac/qgsstacparser.cpp @@ -35,6 +35,10 @@ void QgsStacParser::setData( const QByteArray &data ) { mType = QgsStacObject::Type::Item; } + else if ( mData["type"] == "FeatureCollection" ) + { + mType = QgsStacObject::Type::FeatureCollection; + } else { mType = QgsStacObject::Type::Unknown; @@ -253,41 +257,47 @@ QgsStacCollection QgsStacParser::collection() QgsStacItem QgsStacParser::item() { - if ( mType != QgsStacObject::Type::Item ) + return parseItem( mData ); +} + +QgsStacItem QgsStacParser::parseItem( const nlohmann::json &data ) +{ + if ( mType != QgsStacObject::Type::Item && + mType != QgsStacObject::Type::FeatureCollection ) return QgsStacItem(); try { - const QString ver = QString::fromStdString( mData["stac_version"] ); - const QString id = QString::fromStdString( mData["id"] ); - const QgsGeometry geom = QgsJsonUtils::geometryFromGeoJson( mData["geometry"] ); + const QString ver = QString::fromStdString( data["stac_version"] ); + const QString id = QString::fromStdString( data["id"] ); + const QgsGeometry geom = QgsJsonUtils::geometryFromGeoJson( data["geometry"] ); QgsBox3D bbox; if ( !geom.isNull() ) { - if ( mData["bbox"].size() == 4 ) + if ( data["bbox"].size() == 4 ) { - bbox.setXMinimum( mData["bbox"][0] ); - bbox.setYMinimum( mData["bbox"][1] ); - bbox.setXMaximum( mData["bbox"][2] ); - bbox.setYMaximum( mData["bbox"][3] ); + bbox.setXMinimum( data["bbox"][0] ); + bbox.setYMinimum( data["bbox"][1] ); + bbox.setXMaximum( data["bbox"][2] ); + bbox.setYMaximum( data["bbox"][3] ); } - else if ( mData["bbox"].size() == 6 ) + else if ( data["bbox"].size() == 6 ) { - bbox.setXMinimum( mData["bbox"][0] ); - bbox.setYMinimum( mData["bbox"][1] ); - bbox.setZMinimum( mData["bbox"][2] ); - bbox.setXMaximum( mData["bbox"][3] ); - bbox.setYMaximum( mData["bbox"][4] ); - bbox.setZMaximum( mData["bbox"][5] ); + bbox.setXMinimum( data["bbox"][0] ); + bbox.setYMinimum( data["bbox"][1] ); + bbox.setZMinimum( data["bbox"][2] ); + bbox.setXMaximum( data["bbox"][3] ); + bbox.setYMaximum( data["bbox"][4] ); + bbox.setZMaximum( data["bbox"][5] ); } } - const QVariant properties = QgsJsonUtils::jsonToVariant( mData["properties"] ); + const QVariant properties = QgsJsonUtils::jsonToVariant( data["properties"] ); QVector< QgsStacLink > links; - links.reserve( static_cast( mData["links"].size() ) ); - for ( const auto &link : mData["links"] ) + links.reserve( static_cast( data["links"].size() ) ); + for ( const auto &link : data["links"] ) { QgsStacLink l; l.href = QString::fromStdString( link["href"] ); @@ -298,7 +308,7 @@ QgsStacItem QgsStacParser::item() } QMap< QString, QgsStacAsset > assets; - for ( const auto &asset : mData["assets"].items() ) + for ( const auto &asset : data["assets"].items() ) { const auto value = asset.value(); QgsStacAsset a; @@ -315,14 +325,14 @@ QgsStacItem QgsStacParser::item() QStringList extensions; - for ( const auto &extension : mData["stac_extensions"] ) + for ( const auto &extension : data["stac_extensions"] ) { extensions.append( QString::fromStdString( extension ) ); } item.setStacExtensions( extensions ); - if ( mData.contains( "collection" ) ) - item.setCollection( QString::fromStdString( mData["collection"] ) ); + if ( data.contains( "collection" ) ) + item.setCollection( QString::fromStdString( data["collection"] ) ); return item; } @@ -332,3 +342,41 @@ QgsStacItem QgsStacParser::item() return QgsStacItem(); } } + +QgsStacFeatureCollection QgsStacParser::featureCollection() +{ + if ( mType != QgsStacObject::Type::FeatureCollection ) + return QgsStacFeatureCollection(); + + try + { + QVector< QgsStacLink > links; + links.reserve( static_cast( mData["links"].size() ) ); + for ( const auto &link : mData["links"] ) + { + QgsStacLink l; + l.href = QString::fromStdString( link["href"] ); + l.relation = QString::fromStdString( link["rel"] ); + l.mediaType = link.contains( "type" ) ? QString::fromStdString( link["type"] ) : QString(); + l.title = link.contains( "title" ) ? QString::fromStdString( link["title"] ) : QString(); + links.append( l ); + } + + QVector< QgsStacItem > items; + items.reserve( static_cast( mData["features"].size() ) ); + for ( const auto &item : mData["features"] ) + { + QgsStacItem i = parseItem( item ); + if ( i.isValid() ) + items.append( i ); + } + + QgsStacFeatureCollection fc( items, links ); + return fc; + } + catch ( nlohmann::json::exception &ex ) + { + QgsDebugError( QStringLiteral( "Error parsing FeatureCollection: %1" ).arg( ex.what() ) ); + return QgsStacFeatureCollection(); + } +} diff --git a/src/core/stac/qgsstacparser.h b/src/core/stac/qgsstacparser.h index ab94275d6dbd..b13a630226d4 100644 --- a/src/core/stac/qgsstacparser.h +++ b/src/core/stac/qgsstacparser.h @@ -21,6 +21,7 @@ #include "qgsstacitem.h" #include "qgsstaccollection.h" #include "qgsstaccatalog.h" +#include "qgsstacfeaturecollection.h" class QgsStacParser @@ -33,10 +34,17 @@ class QgsStacParser QgsStacCollection collection(); QgsStacItem item(); + QgsStacFeatureCollection featureCollection(); + QgsStacObject::Type type(); private: + QgsStacItem parseItem( const nlohmann::json &data ); + QgsStacItem parseCatalog( const nlohmann::json &data ); + QgsStacItem parseCollection( const nlohmann::json &data ); + + nlohmann::json mData; QgsStacObject::Type mType = QgsStacObject::Type::Unknown; std::unique_ptr mObject;