Skip to content
Permalink
Browse files

[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).
  • Loading branch information
nyalldawson committed Oct 24, 2017
1 parent 6b23e1f commit 9e4518fe425f71f636ee955bd3d385ae9214f939
@@ -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

};

/************************************************************************
@@ -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
@@ -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();
}
@@ -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,
@@ -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
@@ -139,6 +139,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
void adjustNoDataCellWidth( int row, int column );
bool validate() const;

void insertAvailableOutputFormats();
};


@@ -109,6 +109,11 @@ def testDriverForExtension(self):
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)

0 comments on commit 9e4518f

Please sign in to comment.
You can’t perform that action at this time.