Skip to content
Permalink
Browse files
Implement querySublayers for GDAL provider
  • Loading branch information
nyalldawson committed Jun 23, 2021
1 parent 7ae4e83 commit 09983713cd6796f9edbb183963e5bd6c1d993b99
@@ -27,6 +27,7 @@
#include "qgsvectortiledataitems.h"
#include "qgsproviderregistry.h"
#include "symbology/qgsstyle.h"
#include "qgsprovidersublayerdetails.h"

#include <QFileInfo>
#include <mutex>
@@ -39,15 +40,15 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS

QgsGdalLayerItem::QgsGdalLayerItem( QgsDataItem *parent,
const QString &name, const QString &path, const QString &uri,
QStringList *sublayers )
const QList<QgsProviderSublayerDetails> &sublayers )
: QgsLayerItem( parent, name, path, uri, Qgis::BrowserLayerType::Raster, QStringLiteral( "gdal" ) )
{
mToolTip = uri;
// save sublayers for subsequent access
// if there are sublayers, set populated=false so item can be populated on demand
if ( sublayers && !sublayers->isEmpty() )
if ( !sublayers.isEmpty() )
{
mSublayers = *sublayers;
mSublayers = sublayers;
// We have sublayers: we are able to create children!
mCapabilities |= Qgis::BrowserItemCapability::Fertile;
setState( Qgis::BrowserItemState::NotPopulated );
@@ -81,18 +82,12 @@ QVector<QgsDataItem *> QgsGdalLayerItem::createChildren()
// get children from sublayers
if ( !mSublayers.isEmpty() )
{
QgsDataItem *childItem = nullptr;
QgsDebugMsgLevel( QStringLiteral( "got %1 sublayers" ).arg( mSublayers.count() ), 3 );
for ( int i = 0; i < mSublayers.count(); i++ )
for ( const QgsProviderSublayerDetails &layer : std::as_const( mSublayers ) )
{
const QStringList parts = mSublayers[i].split( QgsDataProvider::sublayerSeparator() );
const QString path = parts[0];
const QString desc = parts[1];
childItem = new QgsGdalLayerItem( this, desc, path, path );
if ( childItem )
{
children.append( childItem );
}
const QString path = layer.name();
const QString desc = layer.description();
children.append( new QgsGdalLayerItem( this, desc, path, path, {} ) );
}
}

@@ -328,11 +323,8 @@ QgsDataItem *QgsGdalDataItemProvider::createDataItem( const QString &pathIn, Qgs
}
}
// add the item
QStringList sublayers;
QgsDebugMsgLevel( QStringLiteral( "adding item name=%1 path=%2" ).arg( name, path ), 2 );
QgsLayerItem *item = new QgsGdalLayerItem( parentItem, name, path, path, &sublayers );
if ( item )
return item;
return new QgsGdalLayerItem( parentItem, name, path, path, {} );
}

// test that file is valid with GDAL
@@ -358,15 +350,12 @@ QgsDataItem *QgsGdalDataItemProvider::createDataItem( const QString &pathIn, Qgs
return nullptr;
}

QStringList sublayers = QgsGdalProvider::subLayers( hDS.get() );
const QList< QgsProviderSublayerDetails > sublayers = QgsGdalProvider::sublayerDetails( hDS.get(), path );
hDS.reset();

QgsDebugMsgLevel( "GdalDataset opened " + path, 2 );

QgsLayerItem *item = new QgsGdalLayerItem( parentItem, name, path, path,
&sublayers );

return item;
return new QgsGdalLayerItem( parentItem, name, path, path, sublayers );
}

///@endcond
@@ -25,25 +25,28 @@
///@cond PRIVATE
#define SIP_NO_FILE

class QgsProviderSublayerDetails;

Q_NOWARN_DEPRECATED_PUSH // setCrs is deprecated
class CORE_EXPORT QgsGdalLayerItem : public QgsLayerItem
{
Q_OBJECT

private:

QStringList mSublayers;

public:
QgsGdalLayerItem( QgsDataItem *parent,
const QString &name, const QString &path, const QString &uri,
QStringList *mSublayers = nullptr );
const QList<QgsProviderSublayerDetails> &sublayers );

bool setCrs( const QgsCoordinateReferenceSystem &crs ) override;

QVector<QgsDataItem *> createChildren() override;

QString layerName() const override;

private:

QList< QgsProviderSublayerDetails > mSublayers;

};
Q_NOWARN_DEPRECATED_POP

@@ -44,6 +44,7 @@
#include "qgsogrutils.h"
#include "qgsruntimeprofiler.h"
#include "qgszipitem.h"
#include "qgsprovidersublayerdetails.h"

#include <QImage>
#include <QColor>
@@ -1639,19 +1640,20 @@ QgsRasterDataProvider::ProviderCapabilities QgsGdalProvider::providerCapabilitie
ProviderCapability::ReloadData;
}

// This is used also by global isValidRasterFileName
QStringList QgsGdalProvider::subLayers( GDALDatasetH dataset )
QList<QgsProviderSublayerDetails> QgsGdalProvider::sublayerDetails( GDALDatasetH dataset, const QString &baseUri )
{
QStringList subLayers;

if ( !dataset )
{
QgsDebugMsg( QStringLiteral( "dataset is nullptr" ) );
return subLayers;
return {};
}

QList<QgsProviderSublayerDetails> res;

char **metadata = GDALGetMetadata( dataset, "SUBDATASETS" );

QVariantMap uriParts = decodeGdalUri( baseUri );

if ( metadata )
{
const int subdatasetCount = CSLCount( metadata ) / 2;
@@ -1661,24 +1663,46 @@ QStringList QgsGdalProvider::subLayers( GDALDatasetH dataset )
const char *desc = CSLFetchNameValue( metadata, CPLSPrintf( "SUBDATASET_%d_DESC", i ) );
if ( name && desc )
{
QString layerName = QString::fromUtf8( name );
QString uri = layerName;
QString layerDesc = QString::fromUtf8( desc );

// For GeoPackage, desc is often "table - identifier" where table=identifier
// In that case, just keep one.
const char *sepPtr = strstr( desc, " - " );
if ( sepPtr && strncmp( desc, sepPtr + 3, strlen( sepPtr + 3 ) ) == 0 )
int sepIdx = layerDesc.indexOf( QLatin1String( " - " ) );
if ( sepIdx > 0 )
{
layerName = layerDesc.left( sepIdx );
layerDesc = layerDesc.mid( sepIdx + 3 );
}
else
{
desc = sepPtr + 3;
// try to extract layer name from a path like 'NETCDF:"/baseUri":cell_node'
sepIdx = layerName.indexOf( baseUri + "\":" );
if ( sepIdx >= 0 )
{
layerName = layerName.mid( layerName.indexOf( baseUri + "\":" ) + baseUri.length() + 2 );
}
}
subLayers << QString::fromUtf8( name ) + QgsDataProvider::sublayerSeparator() + QString::fromUtf8( desc );

QgsProviderSublayerDetails details;
details.setProviderKey( PROVIDER_KEY );
details.setType( QgsMapLayerType::RasterLayer );
details.setName( layerName );
details.setDescription( layerDesc );
details.setLayerNumber( i );

QVariantMap layerUriParts = decodeGdalUri( uri );
uriParts.insert( QStringLiteral( "layerName" ), layerUriParts.value( QStringLiteral( "layerName" ) ) );
uriParts.insert( QStringLiteral( "path" ), layerUriParts.value( QStringLiteral( "path" ) ) );
details.setUri( encodeGdalUri( layerUriParts ) );

res << details;
}
}
}

if ( !subLayers.isEmpty() )
{
QgsDebugMsgLevel( "sublayers:\n " + subLayers.join( "\n " ), 3 );
}

return subLayers;
return res;
}

bool QgsGdalProvider::hasHistogram( int bandNo,
@@ -2319,7 +2343,11 @@ QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList( const QList<int> &lis

QStringList QgsGdalProvider::subLayers() const
{
return mSubLayers;
QStringList res;
res.reserve( mSubLayers.size() );
for ( const QgsProviderSublayerDetails &layer : mSubLayers )
res << layer.name() + QgsDataProvider::sublayerSeparator() + layer.description();
return res;
}

QVariantMap QgsGdalProviderMetadata::decodeUri( const QString &uri ) const
@@ -2587,8 +2615,7 @@ bool QgsGdalProvider::isValidRasterFileName( QString const &fileNameQString, QSt
}
else if ( GDALGetRasterCount( myDataset.get() ) == 0 )
{
QStringList layers = QgsGdalProvider::subLayers( myDataset.get() );
if ( layers.isEmpty() )
if ( QgsGdalProvider::sublayerDetails( myDataset.get(), fileName ).isEmpty() )
{
retErrMsg = QObject::tr( "This raster file has no bands and is invalid as a raster layer." );
return false;
@@ -2960,7 +2987,7 @@ void QgsGdalProvider::initBaseDataset()
}

// get sublayers
mSubLayers = QgsGdalProvider::subLayers( mGdalDataset );
mSubLayers = QgsGdalProvider::sublayerDetails( mGdalDataset, dataSourceUri() );

// check if this file has bands or subdatasets
CPLErrorReset();
@@ -3526,6 +3553,65 @@ QgsProviderMetadata::ProviderCapabilities QgsGdalProviderMetadata::providerCapab
return FileBasedUris;
}

QList<QgsProviderSublayerDetails> QgsGdalProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
{
gdal::dataset_unique_ptr dataset;

QgsGdalProviderBase::registerGdalDrivers();

CPLErrorReset();

QString gdalUri = uri;

// Try to open using VSIFileHandler
QString vsiPrefix = QgsZipItem::vsiPrefix( gdalUri );
if ( !vsiPrefix.isEmpty() )
{
if ( !gdalUri.startsWith( vsiPrefix ) )
gdalUri = vsiPrefix + gdalUri;
}

dataset.reset( QgsGdalProviderBase::gdalOpen( gdalUri, GDAL_OF_READONLY ) );
if ( !dataset )
{
if ( CPLGetLastErrorNo() != CPLE_OpenFailed )
QgsDebugMsg( QStringLiteral( "Error querying sublayers: %1 " ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
return {};
}

const QList< QgsProviderSublayerDetails > res = QgsGdalProvider::sublayerDetails( dataset.get(), uri );
if ( res.empty() )
{
// may not have multiple sublayers, but may still be a single-raster layer dataset
if ( GDALGetRasterCount( dataset.get() ) != 0 )
{
QgsProviderSublayerDetails details;
details.setProviderKey( PROVIDER_KEY );
details.setType( QgsMapLayerType::RasterLayer );
details.setUri( uri );
details.setLayerNumber( 1 );

QString name;
const QVariantMap parts = decodeUri( uri );
if ( parts.contains( QStringLiteral( "path" ) ) )
{
QFileInfo fi( parts.value( QStringLiteral( "path" ) ).toString() );
name = fi.baseName();
}
details.setName( name.isEmpty() ? uri : name );
return {details};
}
else
{
return {};
}
}
else
{
return res;
}
}

QList<QgsDataItemProvider *> QgsGdalProviderMetadata::dataItemProviders() const
{
QList< QgsDataItemProvider * > providers;
@@ -26,6 +26,7 @@
#include "qgscolorrampshader.h"
#include "qgsrasterbandstats.h"
#include "qgsprovidermetadata.h"
#include "qgsprovidersublayerdetails.h"

#include <QString>
#include <QStringList>
@@ -155,7 +156,8 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase
QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const override;
QString htmlMetadata() override;
QStringList subLayers() const override;
static QStringList subLayers( GDALDatasetH dataset );

static QList< QgsProviderSublayerDetails > sublayerDetails( GDALDatasetH dataset, const QString &baseUri );

bool hasStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
@@ -297,7 +299,7 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase
QList<QgsRasterPyramid> mPyramidList;

//! \brief sublayers list saved for subsequent access
QStringList mSubLayers;
QList< QgsProviderSublayerDetails > mSubLayers;

//! Whether a per-dataset mask band is exposed as an alpha band for the point of view of the rest of the application.
bool mMaskBandExposedAsAlpha = false;
@@ -382,6 +384,7 @@ class QgsGdalProviderMetadata final: public QgsProviderMetadata
QList< QgsDataItemProvider * > dataItemProviders() const override;
QList<QPair<QString, QString> > pyramidResamplingMethods() override;
ProviderCapabilities providerCapabilities() const override;
QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const override;
};

///@endcond

0 comments on commit 0998371

Please sign in to comment.