Merge pull request #5839 from alexbruy/raster-extensions

Add methods to get supported raster formats and extensions to QgsRasterFileWriter
This commit is contained in:
Alexander Bruy 2017-12-11 19:54:28 +02:00 committed by GitHub
commit 6b73f78198
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 238 additions and 36 deletions

View File

@ -35,6 +35,13 @@ class QgsRasterFileWriter
WriteCanceled,
};
enum RasterFormatOption
{
SortRecommended,
};
typedef QFlags<QgsRasterFileWriter::RasterFormatOption> RasterFormatOptions;
QgsRasterFileWriter( const QString &outputUrl );
QgsRasterDataProvider *createOneBandRaster( Qgis::DataType dataType,
@ -156,6 +163,49 @@ class QgsRasterFileWriter
:rtype: list of str
%End
static QString filterForDriver( const QString &driverName );
%Docstring
Creates a filter for an GDAL driver key
:rtype: str
%End
struct FilterFormatDetails
{
QString driverName;
%Docstring
Unique driver name
%End
QString filterString;
%Docstring
Filter string for file picker dialogs
%End
};
static QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersAndFormats( RasterFormatOptions options = SortRecommended );
%Docstring
Returns a list or pairs, with format filter string as first element and GDAL format key as second element.
Relies on GDAL_DMD_EXTENSIONS metadata, if it is empty corresponding driver will be skipped even if supported.
The ``options`` argument can be used to control the sorting and filtering of
returned formats.
.. seealso:: :py:func:`supportedOutputRasterLayerExtensions()`
:rtype: list of QgsRasterFileWriter.FilterFormatDetails
%End
static QStringList supportedFormatExtensions( RasterFormatOptions options = SortRecommended );
%Docstring
Returns a list of file extensions for supported formats.
The ``options`` argument can be used to control the sorting and filtering of
returned formats.
.. versionadded:: 3.0
.. seealso:: :py:func:`supportedFiltersAndFormats()`
:rtype: list of str
%End
static QString driverForExtension( const QString &extension );
%Docstring
Returns the GDAL driver name for a specified file ``extension``. E.g. the

View File

@ -31,7 +31,8 @@ from qgis.PyQt.QtCore import QCoreApplication, QObject, pyqtSignal
from qgis.core import (NULL,
QgsApplication,
QgsSettings,
QgsVectorFileWriter)
QgsVectorFileWriter,
QgsRasterFileWriter)
from processing.tools.system import defaultOutputFolder
import processing.tools.dataobjects
@ -170,7 +171,7 @@ class ProcessingConfig:
valuetype=Setting.SELECTION,
options=extensions))
extensions = processing.tools.dataobjects.getSupportedOutputRasterLayerExtensions()
extensions = QgsRasterFileWriter.supportedFormatExtensions()
ProcessingConfig.addSetting(Setting(
ProcessingConfig.tr('General'),
ProcessingConfig.DEFAULT_OUTPUT_RASTER_LAYER_EXT,

View File

@ -29,7 +29,8 @@ __revision__ = '$Format:%H$'
from qgis.core import (QgsProcessing,
QgsProviderRegistry,
QgsProcessingFeatureSourceDefinition,
QgsVectorFileWriter)
QgsVectorFileWriter,
QgsRasterFileWriter)
from qgis.PyQt.QtCore import QCoreApplication
from processing.tools import dataobjects
@ -48,7 +49,7 @@ def getFileFilter(param):
"""
if param.type() == 'multilayer':
if param.layerType() == QgsProcessing.TypeRaster:
exts = dataobjects.getSupportedOutputRasterLayerExtensions()
exts = QgsRasterFileWriter.supportedFormatExtensions()
elif param.layerType() == QgsProcessing.TypeFile:
return tr('All files (*.*)', 'QgsProcessingParameterMultipleLayers')
else:
@ -59,7 +60,12 @@ def getFileFilter(param):
elif param.type() == 'raster':
return QgsProviderRegistry.instance().fileRasterFilters()
elif param.type() == 'rasterDestination':
exts = dataobjects.getSupportedOutputRasterFilters()
if param.provider() is not None:
exts = param.provider().supportedOutputRasterLayerExtensions()
else:
exts = QgsRasterFileWriter.supportedFormatExtensions()
for i in range(len(exts)):
exts[i] = tr('{0} files (*.{1})', 'ParameterRaster').format(exts[i].upper(), exts[i].lower())
return ';;'.join(exts) + ';;' + tr('All files (*.*)')
elif param.type() in ('sink', 'vectorDestination'):
if param.provider() is not None:

View File

@ -105,35 +105,6 @@ def createExpressionContext():
return context
def getSupportedOutputRasterLayerExtensions():
allexts = []
for exts in list(GdalUtils.getSupportedRasters().values()):
for ext in exts:
if ext != 'tif' and ext not in allexts:
allexts.append(ext)
allexts.sort()
allexts.insert(0, 'tif') # tif is the default, should be the first
return allexts
def getSupportedOutputRasterFilters():
"""
Return a list of file filters for supported raster formats.
Supported formats come from Gdal.
:return: a list of strings for Qt file filters.
"""
allFilters = []
supported = GdalUtils.getSupportedOutputRasters()
formatList = sorted(supported.keys())
# Place GTiff as the first format
if 'GTiff' in formatList:
formatList.pop(formatList.index('GTiff'))
formatList.insert(0, 'GTiff')
for f in formatList:
allFilters.append('{0} files (*.{1})'.format(f, ' *.'.join(supported[f])))
return allFilters
def load(fileName, name=None, crs=None, style=None, isRaster=False):
"""Loads a layer/table into the current project, given its file.
"""

View File

@ -18,6 +18,7 @@
#include "qgsprocessingprovider.h"
#include "qgsapplication.h"
#include "qgsvectorfilewriter.h"
#include "qgsrasterfilewriter.h"
#include "qgssettings.h"
QgsProcessingProvider::QgsProcessingProvider( QObject *parent SIP_TRANSFERTHIS )
@ -47,7 +48,7 @@ QString QgsProcessingProvider::longName() const
QStringList QgsProcessingProvider::supportedOutputRasterLayerExtensions() const
{
return QStringList() << QStringLiteral( "tif" );
return QgsRasterFileWriter::supportedFormatExtensions();
}
void QgsProcessingProvider::refreshAlgorithms()
@ -136,4 +137,3 @@ QString QgsProcessingProvider::defaultRasterFileExtension() const
return defaultExtension;
}
}

View File

@ -1023,3 +1023,103 @@ QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
}
return QStringList();
}
QString QgsRasterFileWriter::filterForDriver( const QString &driverName )
{
GDALDriverH drv = GDALGetDriverByName( driverName.toLocal8Bit().data() );
if ( drv )
{
QString drvName = GDALGetDriverLongName( drv );
QString extensionsString = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) );
if ( extensionsString.isEmpty() )
{
return QString();
}
QStringList extensions = extensionsString.split( ' ' );
QString filter = drvName + " (";
for ( const QString &ext : extensions )
{
filter.append( QStringLiteral( "*.%1 *.%2 " ).arg( ext.toLower(), ext.toUpper() ) );
}
filter = filter.trimmed().append( QStringLiteral( ")" ) );
return filter;
}
return QString();
}
QList< QgsRasterFileWriter::FilterFormatDetails > QgsRasterFileWriter::supportedFiltersAndFormats( RasterFormatOptions options )
{
QList< FilterFormatDetails > results;
GDALAllRegister();
int const drvCount = GDALGetDriverCount();
FilterFormatDetails tifFormat;
for ( int i = 0; i < drvCount; ++i )
{
GDALDriverH drv = GDALGetDriver( i );
if ( drv )
{
QString drvName = GDALGetDriverShortName( drv );
char **driverMetadata = GDALGetMetadata( drv, nullptr );
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
{
QString filterString = filterForDriver( drvName );
if ( filterString.isEmpty() )
continue;
FilterFormatDetails details;
details.driverName = drvName;
details.filterString = filterString;
if ( options & SortRecommended )
{
if ( drvName == QLatin1String( "GTiff" ) )
{
tifFormat = details;
continue;
}
}
results << details;
}
}
}
std::sort( results.begin(), results.end(), []( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
{
return a.driverName < b.driverName;
} );
if ( options & SortRecommended )
{
if ( !tifFormat.filterString.isEmpty() )
{
results.insert( 0, tifFormat );
}
}
return results;
}
QStringList QgsRasterFileWriter::supportedFormatExtensions( const RasterFormatOptions options )
{
const auto formats = supportedFiltersAndFormats( options );
QStringList extensions;
QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]*)" ) );
for ( const FilterFormatDetails &format : formats )
{
QString ext = format.filterString;
QRegularExpressionMatch match = rx.match( ext );
if ( !match.hasMatch() )
continue;
QString matched = match.captured( 1 );
extensions << matched;
}
return extensions;
}

View File

@ -54,6 +54,16 @@ class CORE_EXPORT QgsRasterFileWriter
WriteCanceled = 6, //!< Writing was manually canceled
};
/**
* Options for sorting and filtering raster formats.
* \since QGIS 3.0
*/
enum RasterFormatOption
{
SortRecommended = 1 << 1, //!< Use recommended sort order, with extremely commonly used formats listed first
};
Q_DECLARE_FLAGS( RasterFormatOptions, RasterFormatOption )
QgsRasterFileWriter( const QString &outputUrl );
/**
@ -134,6 +144,44 @@ class CORE_EXPORT QgsRasterFileWriter
void setPyramidsConfigOptions( const QStringList &list ) { mPyramidsConfigOptions = list; }
QStringList pyramidsConfigOptions() const { return mPyramidsConfigOptions; }
//! Creates a filter for an GDAL driver key
static QString filterForDriver( const QString &driverName );
/**
* Details of available filters and formats.
* \since QGIS 3.0
*/
struct FilterFormatDetails
{
//! Unique driver name
QString driverName;
//! Filter string for file picker dialogs
QString filterString;
};
/**
* Returns a list or pairs, with format filter string as first element and GDAL format key as second element.
* Relies on GDAL_DMD_EXTENSIONS metadata, if it is empty corresponding driver will be skipped even if supported.
*
* The \a options argument can be used to control the sorting and filtering of
* returned formats.
*
* \see supportedOutputRasterLayerExtensions()
*/
static QList< QgsRasterFileWriter::FilterFormatDetails > supportedFiltersAndFormats( RasterFormatOptions options = SortRecommended );
/**
* Returns a list of file extensions for supported formats.
*
* The \a options argument can be used to control the sorting and filtering of
* returned formats.
*
* \since QGIS 3.0
* \see supportedFiltersAndFormats()
*/
static QStringList supportedFormatExtensions( RasterFormatOptions options = SortRecommended );
/**
* Returns the GDAL driver name for a specified file \a extension. E.g. the
* driver name for the ".tif" extension is "GTiff".

View File

@ -114,6 +114,32 @@ class TestQgsRasterFileWriter(unittest.TestCase):
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GTiff'), ['tiff', 'tif'])
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GPKG'), ['gpkg'])
def testSupportedFiltersAndFormat(self):
# test with formats in recommended order
formats = QgsRasterFileWriter.supportedFiltersAndFormats(QgsRasterFileWriter.SortRecommended)
self.assertEqual(formats[0].filterString, 'GeoTIFF (*.tif *.TIF *.tiff *.TIFF)')
self.assertEqual(formats[0].driverName, 'GTiff')
self.assertTrue('netCDF' in [f.driverName for f in formats])
# alphabetical sorting
formats2 = QgsRasterFileWriter.supportedFiltersAndFormats(QgsRasterFileWriter.RasterFormatOptions())
self.assertTrue(formats2[0].driverName < formats2[1].driverName)
self.assertCountEqual([f.driverName for f in formats], [f.driverName for f in formats2])
self.assertNotEqual(formats2[0].driverName, 'GTiff')
def testSupportedFormatExtensions(self):
formats = QgsRasterFileWriter.supportedFormatExtensions()
self.assertTrue('tif' in formats)
self.assertFalse('exe' in formats)
self.assertEqual(formats[0], 'tif')
self.assertTrue('nc' in formats)
# alphabetical sorting
formats2 = QgsRasterFileWriter.supportedFormatExtensions(QgsRasterFileWriter.RasterFormatOptions())
self.assertTrue(formats2[1] < formats2[2])
self.assertCountEqual(formats, formats2)
self.assertNotEqual(formats2[0], 'tif')
def testImportIntoGpkg(self):
# init target file
test_gpkg = tempfile.mktemp(suffix='.gpkg', dir=self.testDataDir)