[FEATURE] More output format choices in raster save as dialog

Previously only geotiff format was available, even though the
underlying QgsRasterFileWriter/GDAL libraries support other
formats.

This commit exposes those other formats to the dialog so that
users can directly save rasters to them (including everyone's
new BFF, geopackage).
This commit is contained in:
Nyall Dawson 2017-10-24 11:16:38 +10:00
parent 6b23e1f299
commit 9e4518fe42
7 changed files with 131 additions and 15 deletions

View File

@ -165,6 +165,17 @@ class QgsRasterFileWriter
:rtype: str
%End
static QStringList extensionsForFormat( const QString &format );
%Docstring
Returns a list of known file extensions for the given GDAL driver ``format``.
E.g. returns "tif", "tiff" for the format "GTiff".
If no matching format driver is found an empty list will be returned.
.. versionadded:: 3.0
:rtype: list of str
%End
};
/************************************************************************

View File

@ -6750,6 +6750,10 @@ void QgisApp::saveAsRasterFile( QgsRasterLayer *rasterLayer )
fileWriter.setMaxTileWidth( d.maximumTileSizeX() );
fileWriter.setMaxTileHeight( d.maximumTileSizeY() );
}
else
{
fileWriter.setOutputFormat( d.outputFormat() );
}
// TODO: show error dialogs
// TODO: this code should go somewhere else, but probably not into QgsRasterFileWriter

View File

@ -1009,3 +1009,17 @@ QString QgsRasterFileWriter::driverForExtension( const QString &extension )
}
return QString();
}
QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
{
GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
if ( drv )
{
char **driverMetadata = GDALGetMetadata( drv, nullptr );
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
{
return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
}
}
return QStringList();
}

View File

@ -142,6 +142,16 @@ class CORE_EXPORT QgsRasterFileWriter
*/
static QString driverForExtension( const QString &extension );
/**
* Returns a list of known file extensions for the given GDAL driver \a format.
* E.g. returns "tif", "tiff" for the format "GTiff".
*
* If no matching format driver is found an empty list will be returned.
*
* \since QGIS 3.0
*/
static QStringList extensionsForFormat( const QString &format );
private:
QgsRasterFileWriter(); //forbidden
WriterError writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,

View File

@ -23,7 +23,8 @@
#include "qgsrastertransparency.h"
#include "qgsprojectionselectiondialog.h"
#include "qgssettings.h"
#include "qgsrasterfilewriter.h"
#include "cpl_string.h"
#include <gdal.h>
#include <QFileDialog>
@ -74,13 +75,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
toggleResolutionSize();
//only one hardcoded format at the moment
QStringList myFormats;
myFormats << QStringLiteral( "GTiff" );
Q_FOREACH ( const QString &myFormat, myFormats )
{
mFormatComboBox->addItem( myFormat );
}
insertAvailableOutputFormats();
//fill reasonable default values depending on the provider
if ( mDataProvider )
@ -104,7 +99,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
mCreateOptionsWidget->setProvider( mDataProvider->name() );
if ( mDataProvider->name() == QLatin1String( "gdal" ) )
{
mCreateOptionsWidget->setFormat( myFormats[0] );
mCreateOptionsWidget->setFormat( mFormatComboBox->currentData().toString() );
}
mCreateOptionsWidget->setRasterLayer( mRasterLayer );
mCreateOptionsWidget->update();
@ -165,6 +160,68 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
recalcResolutionSize();
}
void QgsRasterLayerSaveAsDialog::insertAvailableOutputFormats()
{
GDALAllRegister();
int nDrivers = GDALGetDriverCount();
QMap< int, QPair< QString, QString > > topPriorityDrivers;
QMap< QString, QString > lowPriorityDrivers;
for ( int i = 0; i < nDrivers; ++i )
{
GDALDriverH driver = GDALGetDriver( i );
if ( driver )
{
char **driverMetadata = GDALGetMetadata( driver, nullptr );
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
{
QString driverShortName = GDALGetDriverShortName( driver );
QString driverLongName = GDALGetDriverLongName( driver );
if ( driverShortName == QLatin1String( "MEM" ) )
{
// in memory rasters are not (yet) supported because the GDAL dataset handle
// would need to be passed directly to QgsRasterLayer (it is not possible to
// close it in raster calculator and reopen the dataset again in raster layer)
continue;
}
else if ( driverShortName == QLatin1String( "VRT" ) )
{
// skip GDAL vrt driver, since we handle that format manually
continue;
}
else if ( driverShortName == QStringLiteral( "GTiff" ) )
{
// always list geotiff first
topPriorityDrivers.insert( 1, qMakePair( driverLongName, driverShortName ) );
}
else if ( driverShortName == QStringLiteral( "GPKG" ) )
{
// and gpkg second
topPriorityDrivers.insert( 2, qMakePair( driverLongName, driverShortName ) );
}
else
{
lowPriorityDrivers.insert( driverLongName, driverShortName );
}
}
}
}
// will be sorted by priority, so that geotiff and geopackage are listed first
for ( auto priorityDriversIt = topPriorityDrivers.constBegin(); priorityDriversIt != topPriorityDrivers.constEnd(); ++priorityDriversIt )
{
mFormatComboBox->addItem( priorityDriversIt.value().first, priorityDriversIt.value().second );
}
// will be sorted by driver name
for ( auto lowPriorityDriversIt = lowPriorityDrivers.constBegin(); lowPriorityDriversIt != lowPriorityDrivers.constEnd(); ++lowPriorityDriversIt )
{
mFormatComboBox->addItem( lowPriorityDriversIt.key(), lowPriorityDriversIt.value() );
}
}
void QgsRasterLayerSaveAsDialog::setValidators()
{
mXResolutionLineEdit->setValidator( new QDoubleValidator( this ) );
@ -212,12 +269,26 @@ void QgsRasterLayerSaveAsDialog::mBrowseButton_clicked()
}
else
{
fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), dirName, tr( "GeoTIFF" ) + " (*.tif *.tiff *.TIF *.TIFF)" );
QStringList extensions = QgsRasterFileWriter::extensionsForFormat( outputFormat() );
QString filter;
QString defaultExt;
if ( extensions.empty() )
filter = tr( "All files (*.*)" );
else
{
filter = QStringLiteral( "%1 (*.%2);;%3" ).arg( mFormatComboBox->currentText(),
extensions.join( QStringLiteral( " *." ) ),
tr( "All files (*.*)" ) );
defaultExt = extensions.at( 0 );
}
fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), dirName, filter );
// ensure the user never omits the extension from the file name
if ( !fileName.isEmpty() && !fileName.endsWith( QLatin1String( ".tif" ), Qt::CaseInsensitive ) && !fileName.endsWith( QLatin1String( ".tiff" ), Qt::CaseInsensitive ) )
QFileInfo fi( fileName );
if ( !fileName.isEmpty() && fi.suffix().isEmpty() )
{
fileName += QLatin1String( ".tif" );
fileName += '.' + defaultExt;
}
}
@ -239,12 +310,12 @@ void QgsRasterLayerSaveAsDialog::mSaveAsLineEdit_textChanged( const QString &tex
}
void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QString &text )
void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QString & )
{
//gdal-specific
if ( mDataProvider && mDataProvider->name() == QLatin1String( "gdal" ) )
{
mCreateOptionsWidget->setFormat( text );
mCreateOptionsWidget->setFormat( outputFormat() );
mCreateOptionsWidget->update();
}
}
@ -296,7 +367,7 @@ QString QgsRasterLayerSaveAsDialog::outputFileName() const
QString QgsRasterLayerSaveAsDialog::outputFormat() const
{
return mFormatComboBox->currentText();
return mFormatComboBox->currentData().toString();
}
QStringList QgsRasterLayerSaveAsDialog::createOptions() const

View File

@ -139,6 +139,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
void adjustNoDataCellWidth( int row, int column );
bool validate() const;
void insertAvailableOutputFormats();
};

View File

@ -109,6 +109,11 @@ class TestQgsRasterFileWriter(unittest.TestCase):
self.assertEqual(QgsRasterFileWriter.driverForExtension('not a format'), '')
self.assertEqual(QgsRasterFileWriter.driverForExtension(''), '')
def testExtensionsForFormat(self):
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('not format'), [])
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GTiff'), ['tiff', 'tif'])
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GPKG'), ['gpkg'])
def testImportIntoGpkg(self):
# init target file
test_gpkg = tempfile.mktemp(suffix='.gpkg', dir=self.testDataDir)