Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Source Manager STAC extras #59690

Merged
merged 9 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/core/stac/qgsstacasset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

#include "qgsstacasset.h"

#include <QUrl>

QgsStacAsset::QgsStacAsset( const QString &href,
const QString &title,
const QString &description,
Expand Down Expand Up @@ -72,3 +74,46 @@ QString QgsStacAsset::formatName() const
return QStringLiteral( "EPT" );
return QString();
}

QgsMimeDataUtils::Uri QgsStacAsset::uri() const
{
QgsMimeDataUtils::Uri uri;
QUrl url( href() );
if ( url.isLocalFile() )
{
uri.uri = href();
}
else if ( formatName() == QLatin1String( "COG" ) )
{
uri.layerType = QStringLiteral( "raster" );
uri.providerKey = QStringLiteral( "gdal" );
if ( href().startsWith( QLatin1String( "http" ), Qt::CaseInsensitive ) ||
href().startsWith( QLatin1String( "ftp" ), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsicurl/%1" ).arg( href() );
}
else if ( href().startsWith( QLatin1String( "s3://" ), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsis3/%1" ).arg( href().mid( 5 ) );
}
else
{
uri.uri = href();
}
}
else if ( formatName() == QLatin1String( "COPC" ) )
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "copc" );
uri.uri = href();
}
else if ( formatName() == QLatin1String( "EPT" ) )
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "ept" );
uri.uri = href();
}
uri.name = title().isEmpty() ? url.fileName() : title();

return uri;
}
7 changes: 7 additions & 0 deletions src/core/stac/qgsstacasset.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define SIP_NO_FILE

#include "qgis_core.h"
#include "qgsmimedatautils.h"

#include <QString>
#include <QStringList>
Expand Down Expand Up @@ -73,6 +74,12 @@ class CORE_EXPORT QgsStacAsset
*/
QString formatName() const;

/**
* Returns a uri for the asset if it is a local or cloud optimized file like COG or COPC
* \since QGIS 3.42
*/
QgsMimeDataUtils::Uri uri() const;

private:
QString mHref;
QString mTitle;
Expand Down
40 changes: 2 additions & 38 deletions src/core/stac/qgsstacitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,50 +191,14 @@ QString QgsStacItem::description() const
QgsMimeDataUtils::UriList QgsStacItem::uris() const
{
QgsMimeDataUtils::UriList uris;
for ( auto it = mAssets.constBegin(); it != mAssets.constEnd(); ++it )
for ( const QgsStacAsset &asset : std::as_const( mAssets ) )
{
QgsMimeDataUtils::Uri uri;
QUrl url( it->href() );
if ( url.isLocalFile() )
{
uri.uri = it->href();
}
else if ( it->formatName() == QLatin1String( "COG" ) )
{
uri.layerType = QStringLiteral( "raster" );
uri.providerKey = QStringLiteral( "gdal" );
if ( it->href().startsWith( QLatin1String( "http" ), Qt::CaseInsensitive ) ||
it->href().startsWith( QLatin1String( "ftp" ), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsicurl/%1" ).arg( it->href() );
}
else if ( it->href().startsWith( QLatin1String( "s3://" ), Qt::CaseInsensitive ) )
{
uri.uri = QStringLiteral( "/vsis3/%1" ).arg( it->href().mid( 5 ) );
}
else
{
uri.uri = it->href();
}
}
else if ( it->formatName() == QLatin1String( "COPC" ) )
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "copc" );
uri.uri = it->href();
}
else if ( it->formatName() == QLatin1String( "EPT" ) )
{
uri.layerType = QStringLiteral( "pointcloud" );
uri.providerKey = QStringLiteral( "ept" );
uri.uri = it->href();
}
QgsMimeDataUtils::Uri uri = asset.uri();

// skip assets with incompatible formats
if ( uri.uri.isEmpty() )
continue;

uri.name = it->title().isEmpty() ? url.fileName() : it->title();
uris.append( uri );
}
return uris;
Expand Down
2 changes: 1 addition & 1 deletion src/core/stac/qgsstacitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class CORE_EXPORT QgsStacItem : public QgsStacObject
QString description() const;

/**
* Returns a list of uris of all assets that have a cloud optimized format like COG or COPC
* Returns a list of uris of all assets that are local or have a cloud optimized format like COG or COPC
* \since QGIS 3.42
*/
QgsMimeDataUtils::UriList uris() const;
Expand Down
82 changes: 3 additions & 79 deletions src/gui/stac/qgsstacdataitemguiprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

#include "qgsstacdataitemguiprovider.h"
#include "moc_qgsstacdataitemguiprovider.cpp"
#include "qgsnetworkcontentfetcherregistry.h"
#include "qgsstaccontroller.h"
#include "qgsstacdataitems.h"
#include "qgsstacconnection.h"
Expand All @@ -25,7 +24,6 @@
#include "qgsstacitem.h"
#include "qgsstacdownloadassetsdialog.h"
#include "qgsstacobjectdetailsdialog.h"
#include "qgsapplication.h"


///@cond PRIVATE
Expand Down Expand Up @@ -195,83 +193,9 @@ void QgsStacDataItemGuiProvider::downloadAssets( QgsDataItem *item, QgsDataItemG

QgsStacDownloadAssetsDialog dialog;
dialog.setStacItem( itemItem->stacItem() );

if ( dialog.exec() == QDialog::Accepted )
{
const QString folder = dialog.selectedFolder();
const QStringList urls = dialog.selectedUrls();
for ( const QString &url : urls )
{
QgsNetworkContentFetcherTask *fetcher = new QgsNetworkContentFetcherTask( url,
itemItem->stacController()->authCfg(),
QgsTask::CanCancel,
tr( "Downloading STAC asset" ) );

connect( fetcher, &QgsNetworkContentFetcherTask::errorOccurred, item, [context]( QNetworkReply::NetworkError, const QString & errorMsg )
{
notify( tr( "Error downloading STAC asset" ),
errorMsg,
context,
Qgis::MessageLevel::Critical );
} );

connect( fetcher, &QgsNetworkContentFetcherTask::fetched, item, [fetcher, folder, context]
{
QNetworkReply *reply = fetcher->reply();
if ( !reply || reply->error() != QNetworkReply::NoError )
{
// canceled or failed
return;
}
else
{
const QString fileName = fetcher->contentDispositionFilename().isEmpty() ? reply->url().fileName() : fetcher->contentDispositionFilename();
QFileInfo fi( fileName );
QFile file( QStringLiteral( "%1/%2" ).arg( folder, fileName ) );
int i = 1;
while ( file.exists() )
{
QString uniqueName = QStringLiteral( "%1/%2(%3)" ).arg( folder, fi.baseName() ).arg( i++ );
if ( !fi.completeSuffix().isEmpty() )
uniqueName.append( QStringLiteral( ".%1" ).arg( fi.completeSuffix() ) );
file.setFileName( uniqueName );
}

bool failed = false;
if ( file.open( QIODevice::WriteOnly ) )
{
const QByteArray data = reply->readAll();
if ( file.write( data ) < 0 )
failed = true;

file.close();
}
else
{
failed = true;
}

if ( failed )
{
notify( tr( "Error downloading STAC asset" ),
tr( "Could not write to file %1" ).arg( file.fileName() ),
context,
Qgis::MessageLevel::Critical );
}
else
{
notify( tr( "STAC asset downloaded" ),
file.fileName(),
context,
Qgis::MessageLevel::Success );
}
}
} );

QgsApplication::taskManager()->addTask( fetcher );
}
}

dialog.setMessageBar( context.messageBar() );
dialog.setAuthCfg( itemItem->stacController()->authCfg() );
dialog.exec();
}

///@endcond
94 changes: 94 additions & 0 deletions src/gui/stac/qgsstacdownloadassetsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
#include "qgsstacdownloadassetsdialog.h"
#include "moc_qgsstacdownloadassetsdialog.cpp"
#include "qgsgui.h"
#include "qgsnetworkcontentfetchertask.h"
#include "qgssettings.h"
#include "qgsproject.h"
#include "qgsmessagebar.h"
#include "qgsapplication.h"

#include <QTreeWidget>
#include <QPushButton>
Expand Down Expand Up @@ -51,6 +54,97 @@ QgsStacDownloadAssetsDialog::QgsStacDownloadAssetsDialog( QWidget *parent ) :
this, &QgsStacDownloadAssetsDialog::showContextMenu );
}

void QgsStacDownloadAssetsDialog::accept()
{
const QString folder = selectedFolder();
const QStringList urls = selectedUrls();
for ( const QString &url : urls )
{
QgsNetworkContentFetcherTask *fetcher = new QgsNetworkContentFetcherTask( url,
mAuthCfg,
QgsTask::CanCancel,
tr( "Downloading STAC asset" ) );

connect( fetcher, &QgsNetworkContentFetcherTask::errorOccurred, fetcher, [bar = mMessageBar]( QNetworkReply::NetworkError, const QString & errorMsg )
{
if ( bar )
bar->pushMessage(
tr( "Error downloading STAC asset" ),
errorMsg,
Qgis::MessageLevel::Critical );
} );

connect( fetcher, &QgsNetworkContentFetcherTask::fetched, fetcher, [fetcher, folder, bar = mMessageBar]
{
QNetworkReply *reply = fetcher->reply();
if ( !reply || reply->error() != QNetworkReply::NoError )
{
// canceled or failed
return;
}
else
{
const QString fileName = fetcher->contentDispositionFilename().isEmpty() ? reply->url().fileName() : fetcher->contentDispositionFilename();
QFileInfo fi( fileName );
QFile file( QStringLiteral( "%1/%2" ).arg( folder, fileName ) );
int i = 1;
while ( file.exists() )
{
QString uniqueName = QStringLiteral( "%1/%2(%3)" ).arg( folder, fi.baseName() ).arg( i++ );
if ( !fi.completeSuffix().isEmpty() )
uniqueName.append( QStringLiteral( ".%1" ).arg( fi.completeSuffix() ) );
file.setFileName( uniqueName );
}

bool failed = false;
if ( file.open( QIODevice::WriteOnly ) )
{
const QByteArray data = reply->readAll();
if ( file.write( data ) < 0 )
failed = true;

file.close();
}
else
{
failed = true;
}

if ( failed )
{
if ( bar )
bar->pushMessage(
tr( "Error downloading STAC asset" ),
tr( "Could not write to file %1" ).arg( file.fileName() ),
Qgis::MessageLevel::Critical );
}
else
{
if ( bar )
bar->pushMessage(
tr( "STAC asset downloaded" ),
file.fileName(),
Qgis::MessageLevel::Success );
}
}
} );

QgsApplication::taskManager()->addTask( fetcher );
}

QDialog::accept();
}

void QgsStacDownloadAssetsDialog::setAuthCfg( const QString &authCfg )
{
mAuthCfg = authCfg;
}

void QgsStacDownloadAssetsDialog::setMessageBar( QgsMessageBar *bar )
{
mMessageBar = bar;
}

void QgsStacDownloadAssetsDialog::setStacItem( QgsStacItem *stacItem )
{
if ( ! stacItem )
Expand Down
7 changes: 7 additions & 0 deletions src/gui/stac/qgsstacdownloadassetsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <QDialog>

class QgsMessageBar;

class QgsStacDownloadAssetsDialog : public QDialog, private Ui::QgsStacDownloadAssetsDialog
{
Expand All @@ -32,6 +33,10 @@ class QgsStacDownloadAssetsDialog : public QDialog, private Ui::QgsStacDownloadA
public:
explicit QgsStacDownloadAssetsDialog( QWidget *parent = nullptr );

void accept() override;

void setAuthCfg( const QString &authCfg );
void setMessageBar( QgsMessageBar *bar );
void setStacItem( QgsStacItem *stacItem );
QString selectedFolder();
QStringList selectedUrls();
Expand All @@ -44,6 +49,8 @@ class QgsStacDownloadAssetsDialog : public QDialog, private Ui::QgsStacDownloadA
void deselectAll();

QMenu *mContextMenu = nullptr;
QString mAuthCfg;
QgsMessageBar *mMessageBar = nullptr;
};

///@endcond
Expand Down
Loading
Loading