Skip to content

Commit

Permalink
Merge pull request #5430 from nyalldawson/raster_formats
Browse files Browse the repository at this point in the history
[FEATURE] More output format choices in raster save as dialog
  • Loading branch information
nyalldawson committed Dec 2, 2017
2 parents 6b23e1f + 5f77a48 commit 65a0c06
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 15 deletions.
11 changes: 11 additions & 0 deletions python/core/raster/qgsrasterfilewriter.sip
Expand Up @@ -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

};

/************************************************************************
Expand Down
1 change: 1 addition & 0 deletions python/gui/qgsrasterlayersaveasdialog.sip
Expand Up @@ -39,6 +39,7 @@ class QgsRasterLayerSaveAsDialog: QDialog
const QgsCoordinateReferenceSystem &currentCrs,
QWidget *parent /TransferThis/ = 0,
Qt::WindowFlags f = 0 );
~QgsRasterLayerSaveAsDialog();

Mode mode() const;
%Docstring
Expand Down
4 changes: 4 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/core/raster/qgsrasterfilewriter.cpp
Expand Up @@ -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();
}
10 changes: 10 additions & 0 deletions src/core/raster/qgsrasterfilewriter.h
Expand Up @@ -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,
Expand Down
110 changes: 95 additions & 15 deletions src/gui/qgsrasterlayersaveasdialog.cpp
Expand Up @@ -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>
Expand Down Expand Up @@ -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 )
Expand All @@ -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();
Expand Down Expand Up @@ -163,6 +158,77 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsRasterLayerSaveAsDialog::extentChanged );

recalcResolutionSize();

QgsSettings settings;
restoreGeometry( settings.value( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ) ).toByteArray() );
}

QgsRasterLayerSaveAsDialog::~QgsRasterLayerSaveAsDialog()
{
QgsSettings settings;
settings.setValue( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ), saveGeometry() );
}

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()
Expand Down Expand Up @@ -212,12 +278,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;
}
}

Expand All @@ -239,12 +319,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();
}
}
Expand Down Expand Up @@ -296,7 +376,7 @@ QString QgsRasterLayerSaveAsDialog::outputFileName() const

QString QgsRasterLayerSaveAsDialog::outputFormat() const
{
return mFormatComboBox->currentText();
return mFormatComboBox->currentData().toString();
}

QStringList QgsRasterLayerSaveAsDialog::createOptions() const
Expand Down
2 changes: 2 additions & 0 deletions src/gui/qgsrasterlayersaveasdialog.h
Expand Up @@ -58,6 +58,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
const QgsCoordinateReferenceSystem &currentCrs,
QWidget *parent SIP_TRANSFERTHIS = nullptr,
Qt::WindowFlags f = 0 );
~QgsRasterLayerSaveAsDialog();

Mode mode() const;
int nColumns() const;
Expand Down Expand Up @@ -139,6 +140,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
void adjustNoDataCellWidth( int row, int column );
bool validate() const;

void insertAvailableOutputFormats();
};


Expand Down
5 changes: 5 additions & 0 deletions tests/src/python/test_qgsrasterfilewriter.py
Expand Up @@ -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)
Expand Down

0 comments on commit 65a0c06

Please sign in to comment.