Skip to content

Commit

Permalink
[GDAL/OGR providers] Improve list of extensions, remove duplicates, s…
Browse files Browse the repository at this point in the history
…upport netCDF vector (fixes #17000)

- Read GDAL 2.0 GDAL_DMD_EXTENSIONS metadata item to retrieve a list of extensions
- Remove 'duplicated' drivers from list (such as KML/LIBKML, DGN/DGNv8)
- Support netCDF as a vector format (in addition to raster)
- For OGR formats, dynamically build list for unknown drivers ('static' list kept for now)
  • Loading branch information
rouault committed Nov 10, 2017
1 parent 2ca4ee5 commit 0ba03e5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 119 deletions.
6 changes: 4 additions & 2 deletions src/providers/gdal/qgsgdaldataitems.cpp
Expand Up @@ -256,8 +256,10 @@ QGISEXTERN QgsDataItem *dataItem( QString path, QgsDataItem *parentItem )
// return item without testing if:
// scanExtSetting
// or zipfile and scan zip == "Basic scan"
if ( scanExtSetting ||
( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) )
// netCDF files can be both raster or vector, so fallback to opening
if ( ( scanExtSetting ||
( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) ) &&
suffix != QLatin1String( "nc" ) )
{
// Skip this layer if it's handled by ogr:
if ( ogrSupportedDbLayersExtensions.contains( suffix ) )
Expand Down
155 changes: 46 additions & 109 deletions src/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -1922,14 +1922,6 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS

GDALDriverH myGdalDriver; // current driver

char **myGdalDriverMetadata; // driver metadata strings

QString myGdalDriverLongName( QLatin1String( "" ) ); // long name for the given driver
QString myGdalDriverExtension( QLatin1String( "" ) ); // file name extension for given driver
QString myGdalDriverDescription; // QString wrapper of GDAL driver description

QStringList metadataTokens; // essentially the metadata string delimited by '='

QStringList catchallFilter; // for Any file(*.*), but also for those
// drivers with no specific file filter

Expand All @@ -1943,10 +1935,6 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS
// theoreticaly we can open those files because there exists a
// driver for them, the user will have to use the "All Files" to
// open datasets with no explicitly defined file name extension.
// Note that file name extension strings are of the form
// "DMD_EXTENSION=.*". We'll also store the long name of the
// driver, which will be found in DMD_LONGNAME, which will have the
// same form.

fileFiltersString.clear();

Expand All @@ -1965,100 +1953,75 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS
}

// in GDAL 2.0 vector and mixed drivers are returned by GDALGetDriver, so filter out non-raster drivers
// TODO also make sure drivers are not loaded unnecessarily (as GDALAllRegister() and OGRRegisterAll load all drivers)
if ( QString( GDALGetMetadataItem( myGdalDriver, GDAL_DCAP_RASTER, nullptr ) ) != QLatin1String( "YES" ) )
continue;

// now we need to see if the driver is for something currently
// supported; if not, we give it a miss for the next driver

myGdalDriverDescription = GDALGetDescription( myGdalDriver );
// QgsDebugMsg(QString("got driver string %1").arg(myGdalDriverDescription));

myGdalDriverExtension.clear();
myGdalDriverLongName.clear();
QString myGdalDriverDescription = GDALGetDescription( myGdalDriver );
if ( myGdalDriverDescription == QLatin1String( "BIGGIF" ) )
{
// BIGGIF is a technical driver. The plain GIF driver will do
continue;
}

myGdalDriverMetadata = GDALGetMetadata( myGdalDriver, nullptr );
// QgsDebugMsg(QString("got driver string %1").arg(myGdalDriverDescription));

// presumably we know we've run out of metadta if either the
// address is 0, or the first character is null
while ( myGdalDriverMetadata && myGdalDriverMetadata[0] )
QString myGdalDriverExtensions = GDALGetMetadataItem( myGdalDriver, GDAL_DMD_EXTENSIONS, "" );
QString myGdalDriverLongName = GDALGetMetadataItem( myGdalDriver, GDAL_DMD_LONGNAME, "" );
// remove any superfluous (.*) strings at the end as
// they'll confuse QFileDialog::getOpenFileNames()
myGdalDriverLongName.remove( QRegExp( "\\(.*\\)$" ) );

// if we have both the file name extension and the long name,
// then we've all the information we need for the current
// driver; therefore emit a file filter string and move to
// the next driver
if ( !( myGdalDriverExtensions.isEmpty() || myGdalDriverLongName.isEmpty() ) )
{
metadataTokens = QString( *myGdalDriverMetadata ).split( '=', QString::SkipEmptyParts );
// QgsDebugMsg(QString("\t%1").arg(*myGdalDriverMetadata));

// XXX add check for malformed metadataTokens
const QStringList splitExtensions = myGdalDriverExtensions.split( ' ', QString::SkipEmptyParts );

// Note that it's oddly possible for there to be a
// DMD_EXTENSION with no corresponding defined extension
// string; so we check that there're more than two tokens.
// XXX add check for SDTS; in that case we want (*CATD.DDF)
QString glob;

if ( metadataTokens.count() > 1 )
for ( const QString &ext : splitExtensions )
{
if ( "DMD_EXTENSION" == metadataTokens[0] )
{
myGdalDriverExtension = metadataTokens[1];

}
else if ( "DMD_LONGNAME" == metadataTokens[0] )
{
myGdalDriverLongName = metadataTokens[1];

// remove any superfluous (.*) strings at the end as
// they'll confuse QFileDialog::getOpenFileNames()

myGdalDriverLongName.remove( QRegExp( "\\(.*\\)$" ) );
}
// This hacking around that removes '/' is no longer necessary with GDAL 2.3
extensions << QString( ext ).remove( '/' ).remove( '*' ).remove( '.' );
if ( !glob.isEmpty() )
glob += QLatin1String( " " );
glob += "*." + QString( ext ).replace( '/', QLatin1String( " *." ) );
}

// if we have both the file name extension and the long name,
// then we've all the information we need for the current
// driver; therefore emit a file filter string and move to
// the next driver
if ( !( myGdalDriverExtension.isEmpty() || myGdalDriverLongName.isEmpty() ) )
// Add only the first JP2 driver found to the filter list (it's the one GDAL uses)
if ( myGdalDriverDescription == QLatin1String( "JPEG2000" ) ||
myGdalDriverDescription.startsWith( QLatin1String( "JP2" ) ) ) // JP2ECW, JP2KAK, JP2MrSID
{
// XXX add check for SDTS; in that case we want (*CATD.DDF)
QString glob = "*." + myGdalDriverExtension.replace( '/', QLatin1String( " *." ) );
extensions << myGdalDriverExtension.remove( '/' ).remove( '*' ).remove( '.' );
// Add only the first JP2 driver found to the filter list (it's the one GDAL uses)
if ( myGdalDriverDescription == QLatin1String( "JPEG2000" ) ||
myGdalDriverDescription.startsWith( QLatin1String( "JP2" ) ) ) // JP2ECW, JP2KAK, JP2MrSID
{
if ( jp2Driver )
break; // skip if already found a JP2 driver
if ( jp2Driver )
continue; // skip if already found a JP2 driver

jp2Driver = myGdalDriver; // first JP2 driver found
jp2Driver = myGdalDriver; // first JP2 driver found
if ( !glob.contains( "j2k" ) )
{
glob += QLatin1String( " *.j2k" ); // add alternate extension
extensions << QStringLiteral( "j2k" );
}
else if ( myGdalDriverDescription == QLatin1String( "GTiff" ) )
{
glob += QLatin1String( " *.tiff" );
extensions << QStringLiteral( "tiff" );
}
else if ( myGdalDriverDescription == QLatin1String( "JPEG" ) )
{
glob += QLatin1String( " *.jpeg" );
extensions << QStringLiteral( "jpeg" );
}
else if ( myGdalDriverDescription == QLatin1String( "VRT" ) )
{
glob += QLatin1String( " *.ovr" );
extensions << QStringLiteral( "ovr" );
}

fileFiltersString += createFileFilter_( myGdalDriverLongName, glob );

break; // ... to next driver, if any.
}
else if ( myGdalDriverDescription == QLatin1String( "VRT" ) )
{
glob += QLatin1String( " *.ovr" );
extensions << QStringLiteral( "ovr" );
}

++myGdalDriverMetadata;
fileFiltersString += createFileFilter_( myGdalDriverLongName, glob );

}

} // each metadata item

//QgsDebugMsg(QString("got driver Desc=%1 LongName=%2").arg(myGdalDriverDescription).arg(myGdalDriverLongName));

if ( myGdalDriverExtension.isEmpty() && !myGdalDriverLongName.isEmpty() )
if ( myGdalDriverExtensions.isEmpty() && !myGdalDriverLongName.isEmpty() )
{
// Then what we have here is a driver with no corresponding
// file extension; e.g., GRASS. In which case we append the
Expand All @@ -2071,29 +2034,9 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS
// DMD_EXTENSION; so let's check for them here and handle
// them appropriately

// USGS DEMs use "*.dem"
if ( myGdalDriverDescription.startsWith( QLatin1String( "USGSDEM" ) ) )
{
fileFiltersString += createFileFilter_( myGdalDriverLongName, QStringLiteral( "*.dem" ) );
extensions << QStringLiteral( "dem" );
}
else if ( myGdalDriverDescription.startsWith( QLatin1String( "DTED" ) ) )
{
// DTED use "*.dt0, *.dt1, *.dt2"
QString glob = QStringLiteral( "*.dt0" );
glob += QLatin1String( " *.dt1" );
glob += QLatin1String( " *.dt2" );
fileFiltersString += createFileFilter_( myGdalDriverLongName, glob );
extensions << QStringLiteral( "dt0" ) << QStringLiteral( "dt1" ) << QStringLiteral( "dt2" );
}
else if ( myGdalDriverDescription.startsWith( QLatin1String( "MrSID" ) ) )
{
// MrSID use "*.sid"
fileFiltersString += createFileFilter_( myGdalDriverLongName, QStringLiteral( "*.sid" ) );
extensions << QStringLiteral( "sid" );
}
else if ( myGdalDriverDescription.startsWith( QLatin1String( "EHdr" ) ) )
if ( myGdalDriverDescription.startsWith( QLatin1String( "EHdr" ) ) )
{
// Fixed in GDAL 2.3
fileFiltersString += createFileFilter_( myGdalDriverLongName, QStringLiteral( "*.bil" ) );
extensions << QStringLiteral( "bil" );
}
Expand All @@ -2102,12 +2045,6 @@ void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QS
fileFiltersString += createFileFilter_( myGdalDriverLongName, QStringLiteral( "hdr.adf" ) );
wildcards << QStringLiteral( "hdr.adf" );
}
else if ( myGdalDriverDescription == QLatin1String( "HDF4" ) )
{
// HDF4 extension missing in driver metadata
fileFiltersString += createFileFilter_( myGdalDriverLongName, QStringLiteral( "*.hdf" ) );
extensions << QStringLiteral( "hdf" );
}
else
{
catchallFilter << QString( GDALGetDescription( myGdalDriver ) );
Expand Down
6 changes: 4 additions & 2 deletions src/providers/ogr/qgsogrdataitems.cpp
Expand Up @@ -598,8 +598,10 @@ QGISEXTERN QgsDataItem *dataItem( QString path, QgsDataItem *parentItem )

// Fast track: return item without testing if:
// scanExtSetting or zipfile and scan zip == "Basic scan"
if ( scanExtSetting ||
( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) )
// netCDF files can be both raster or vector, so fallback to opening
if ( ( scanExtSetting ||
( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) ) &&
suffix != QLatin1String( "nc" ) )
{
// if this is a VRT file make sure it is vector VRT to avoid duplicates
if ( suffix == QLatin1String( "vrt" ) )
Expand Down
62 changes: 56 additions & 6 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -2239,6 +2239,9 @@ QString createFilters( const QString &type )
// open datasets with no explicitly defined file name extension.
QgsDebugMsg( QString( "Driver count: %1" ).arg( OGRGetDriverCount() ) );

bool kmlFound = false;
bool dwgFound = false;
bool dgnFound = false;
for ( int i = 0; i < OGRGetDriverCount(); ++i )
{
driver = OGRGetDriver( i );
Expand Down Expand Up @@ -2319,11 +2322,15 @@ QString createFilters( const QString &type )
sFileFilters += createFileFilter_( QObject::tr( "GeoRSS" ), QStringLiteral( "*.xml" ) );
sExtensions << QStringLiteral( "xml" );
}
else if ( driverName.startsWith( QLatin1String( "GML" ) ) )
else if ( driverName == QLatin1String( "GML" ) )
{
sFileFilters += createFileFilter_( QObject::tr( "Geography Markup Language [GML]" ), QStringLiteral( "*.gml" ) );
sExtensions << QStringLiteral( "gml" );
}
else if ( driverName == QLatin1String( "GMLAS" ) )
{
continue;
}
else if ( driverName.startsWith( QLatin1String( "GMT" ) ) )
{
sFileFilters += createFileFilter_( QObject::tr( "Generic Mapping Tools [GMT]" ), QStringLiteral( "*.gmt" ) );
Expand Down Expand Up @@ -2361,8 +2368,11 @@ QString createFilters( const QString &type )
{
sDatabaseDrivers += QObject::tr( "Ingres" ) + ",Ingres;";
}
else if ( driverName.startsWith( QLatin1String( "KML" ) ) )
else if ( driverName == QLatin1String( "KML" ) || driverName == QLatin1String( "LIBKML" ) )
{
if ( kmlFound )
continue;
kmlFound = true;
sFileFilters += createFileFilter_( QObject::tr( "Keyhole Markup Language [KML]" ), QStringLiteral( "*.kml *.kmz" ) );
sExtensions << QStringLiteral( "kml" ) << QStringLiteral( "kmz" );
}
Expand All @@ -2371,8 +2381,11 @@ QString createFilters( const QString &type )
sFileFilters += createFileFilter_( QObject::tr( "Mapinfo File" ), QStringLiteral( "*.mif *.tab" ) );
sExtensions << QStringLiteral( "mif" ) << QStringLiteral( "tab" );
}
else if ( driverName.startsWith( QLatin1String( "DGN" ) ) )
else if ( driverName == QLatin1String( "DGN" ) || driverName == QLatin1String( "DGNV8" ) )
{
if ( dgnFound )
continue;
dgnFound = true;
sFileFilters += createFileFilter_( QObject::tr( "Microstation DGN" ), QStringLiteral( "*.dgn" ) );
sExtensions << QStringLiteral( "dgn" );
}
Expand Down Expand Up @@ -2561,9 +2574,46 @@ QString createFilters( const QString &type )
}
else
{
// NOP, we don't know anything about the current driver
// with regards to a proper file filter string
QgsDebugMsg( QString( "Unknown driver %1 for file filters." ).arg( driverName ) );
if ( driverName == QLatin1String( "CAD" ) || driverName == QLatin1String( "DWG" ) )
{
if ( dwgFound )
continue;
dwgFound = true;
}

QString myGdalDriverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
QString myGdalDriverLongName = GDALGetMetadataItem( driver, GDAL_DMD_LONGNAME, "" );
if ( !( myGdalDriverExtensions.isEmpty() || myGdalDriverLongName.isEmpty() ) )
{
const QStringList splitExtensions = myGdalDriverExtensions.split( ' ', QString::SkipEmptyParts );

QString glob;

for ( const QString &ext : splitExtensions )
{
sExtensions << ext;
if ( !glob.isEmpty() )
glob += QLatin1String( " " );
glob += "*." + ext;
}

if ( driverName == QLatin1String( "JPEG2000" ) ||
driverName.startsWith( QLatin1String( "JP2" ) ) )
{
// Skip over JPEG2000 drivers, as their vector capabilities are just
// a marginal use case
continue;
}

sFileFilters += createFileFilter_( myGdalDriverLongName, glob );

}
else
{
// NOP, we don't know anything about the current driver
// with regards to a proper file filter string
QgsDebugMsg( QString( "Unknown driver %1 for file filters." ).arg( driverName ) );
}
}

} // each loaded OGR driver
Expand Down

0 comments on commit 0ba03e5

Please sign in to comment.