Skip to content
Permalink
Browse files

[GDAL provider] Use subdataset description for UI when listing sublay…

…ers (fixes #41428)

Note: this change is somewhat breaking since it changes the format of
the return of sublayers() for the GDAL provider to be
SUBDATASET_NAME!!::!!SUBDATASEC_DESC
whereas it was previously
SUBDATASET_NAME
  • Loading branch information
rouault authored and nyalldawson committed Feb 11, 2021
1 parent b6335dd commit 3f42393a9237db8aec60b377f88728fd76e5825f
@@ -5968,49 +5968,13 @@ QList< QgsMapLayer * > QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
layers.reserve( sublayers.size() );
for ( int i = 0; i < sublayers.size(); i++ )
{
// simplify raster sublayer name - should add a function in gdal provider for this?
// code is copied from QgsGdalLayerItem::createChildren
QString name = sublayers[i];
QString path = layer->source();
// if netcdf/hdf use all text after filename
// for hdf4 it would be best to get description, because the subdataset_index is not very practical
if ( name.startsWith( QLatin1String( "netcdf" ), Qt::CaseInsensitive ) ||
name.startsWith( QLatin1String( "hdf" ), Qt::CaseInsensitive ) )
{
name = name.mid( name.indexOf( path ) + path.length() + 1 );
}
else if ( name.startsWith( QLatin1String( "GPKG" ), Qt::CaseInsensitive ) )
{
const auto parts { name.split( ':' ) };
if ( parts.count() >= 3 )
{
name = parts.at( parts.count( ) - 1 );
}
}
else
{
// remove driver name and file name
name.remove( name.split( QgsDataProvider::sublayerSeparator() )[0] );
name.remove( path );
}
// remove any : or " left over
if ( name.startsWith( ':' ) )
name.remove( 0, 1 );

if ( name.startsWith( '\"' ) )
name.remove( 0, 1 );

if ( name.endsWith( ':' ) )
name.chop( 1 );

if ( name.endsWith( '\"' ) )
name.chop( 1 );

names << name;
const QStringList parts = sublayers[i].split( QgsDataProvider::sublayerSeparator() );
const QString desc = parts[1];
names << desc;

QgsSublayersDialog::LayerDefinition def;
def.layerId = i;
def.layerName = name;
def.layerName = desc;
layers << def;
}

@@ -6020,7 +5984,6 @@ QList< QgsMapLayer * > QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
{
// create more informative layer names, containing filename as well as sublayer name
QRegExp rx( "\"(.*)\"" );
QString uri, name;

QgsLayerTreeGroup *group = nullptr;
bool addToGroup = settings.value( QStringLiteral( "/qgis/openSublayersInGroup" ), true ).toBool();
@@ -6034,18 +5997,21 @@ QList< QgsMapLayer * > QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
for ( const QgsSublayersDialog::LayerDefinition &def : constSelection )
{
int i = def.layerId;
if ( rx.indexIn( sublayers[i] ) != -1 )

const QStringList parts = sublayers[i].split( QgsDataProvider::sublayerSeparator() );
const QString path = parts[0];
QString name = path;
if ( rx.indexIn( name ) != -1 )
{
uri = rx.cap( 1 );
name = sublayers[i];
const QString uri = rx.cap( 1 );
name.replace( uri, QFileInfo( uri ).completeBaseName() );
}
else
{
name = names[i];
}

QgsRasterLayer *rlayer = new QgsRasterLayer( sublayers[i], name );
QgsRasterLayer *rlayer = new QgsRasterLayer( path, name );
if ( rlayer && rlayer->isValid() )
{
if ( addToGroup )
@@ -84,25 +84,10 @@ QVector<QgsDataItem *> QgsGdalLayerItem::createChildren()
QgsDebugMsgLevel( QStringLiteral( "got %1 sublayers" ).arg( mSublayers.count() ), 3 );
for ( int i = 0; i < mSublayers.count(); i++ )
{
QString name = mSublayers[i];
// if netcdf/hdf use all text after filename
// for hdf4 it would be best to get description, because the subdataset_index is not very practical
if ( name.startsWith( QLatin1String( "netcdf" ), Qt::CaseInsensitive ) ||
name.startsWith( QLatin1String( "hdf" ), Qt::CaseInsensitive ) )
name = name.mid( name.indexOf( mPath ) + mPath.length() + 1 );
else
{
// remove driver name and file name and initial ':'
name.remove( name.split( QgsDataProvider::sublayerSeparator() )[0] + ':' );
name.remove( mPath );
}
// remove any : or " left over
if ( name.startsWith( ':' ) ) name.remove( 0, 1 );
if ( name.startsWith( '\"' ) ) name.remove( 0, 1 );
if ( name.endsWith( ':' ) ) name.chop( 1 );
if ( name.endsWith( '\"' ) ) name.chop( 1 );

childItem = new QgsGdalLayerItem( this, name, mSublayers[i], mSublayers[i] );
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 );
@@ -1681,13 +1681,21 @@ QStringList QgsGdalProvider::subLayers( GDALDatasetH dataset )

if ( metadata )
{
for ( int i = 0; metadata[i]; i++ )
const int subdatasetCount = CSLCount( metadata ) / 2;
for ( int i = 1; i <= subdatasetCount; i++ )
{
QString layer = QString::fromUtf8( metadata[i] );
int pos = layer.indexOf( QLatin1String( "_NAME=" ) );
if ( pos >= 0 )
const char *name = CSLFetchNameValue( metadata, CPLSPrintf( "SUBDATASET_%d_NAME", i ) );
const char *desc = CSLFetchNameValue( metadata, CPLSPrintf( "SUBDATASET_%d_DESC", i ) );
if ( name && desc )
{
subLayers << layer.mid( pos + 6 );
// 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 )
{
desc = sepPtr + 3;
}
subLayers << QString::fromUtf8( name ) + QgsDataProvider::sublayerSeparator() + QString::fromUtf8( desc );
}
}
}
@@ -128,17 +128,22 @@ void TestQgsRasterSubLayer::subLayersList()
// Layer with sublayers is not valid
//QVERIFY( mpRasterLayer->isValid() );
QStringList expected;
// Sublayer format: NETCDF:"/path/to/landsat2.nc":Band1
// NETCDF:"c:/path/to/landsat2.nc":Band1
// File path is delicate on Windows -> compare only sublayers
expected << QStringLiteral( "Band1" );
expected << QStringLiteral( "Band2" );
// Sublayer format: NETCDF:"/path/to/landsat2.nc":Band1!!::!![200x200] Band1 (8-bit integer)
// NETCDF:"c:/path/to/landsat2.nc":Band1!!::!![200x200] Band1 (8-bit integer)
// File path is delicate on Windows -> compare only end of string

expected << QStringLiteral( ":Band1!!::!![200x200] Band1 (8-bit integer)" );
expected << QStringLiteral( ":Band2!!::!![200x200] Band2 (8-bit integer)" );

QStringList sublayers;
Q_FOREACH ( const QString &s, mpRasterLayer->subLayers() )
{
qDebug() << "sublayer: " << s;
sublayers << s.split( ':' ).last();
int pos = s.indexOf( QLatin1String( ":Band" ) );
if ( pos > 0 )
sublayers << s.mid( pos );
else
sublayers << s;
}
qDebug() << "sublayers: " << sublayers.join( QLatin1Char( ',' ) );
mReport += QStringLiteral( "sublayers:<br>%1<br>\n" ).arg( sublayers.join( QLatin1String( "<br>" ) ) );
@@ -156,6 +161,7 @@ void TestQgsRasterSubLayer::checkStats()
QString sublayerUri = mpRasterLayer->subLayers().value( 0 );
mReport += "sublayer: " + sublayerUri + "<br>\n";

sublayerUri = sublayerUri.split( QgsDataProvider::sublayerSeparator() )[0];
QgsRasterLayer *sublayer = new QgsRasterLayer( sublayerUri, QStringLiteral( "Sublayer 1" ) );

QgsRasterBandStats myStatistics = sublayer->dataProvider()->bandStatistics( 1,

0 comments on commit 3f42393

Please sign in to comment.