Skip to content

Commit 65a0c06

Browse files
authoredDec 2, 2017
Merge pull request #5430 from nyalldawson/raster_formats
[FEATURE] More output format choices in raster save as dialog
2 parents 6b23e1f + 5f77a48 commit 65a0c06

File tree

8 files changed

+142
-15
lines changed

8 files changed

+142
-15
lines changed
 

‎python/core/raster/qgsrasterfilewriter.sip

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ class QgsRasterFileWriter
165165
:rtype: str
166166
%End
167167

168+
static QStringList extensionsForFormat( const QString &format );
169+
%Docstring
170+
Returns a list of known file extensions for the given GDAL driver ``format``.
171+
E.g. returns "tif", "tiff" for the format "GTiff".
172+
173+
If no matching format driver is found an empty list will be returned.
174+
175+
.. versionadded:: 3.0
176+
:rtype: list of str
177+
%End
178+
168179
};
169180

170181
/************************************************************************

‎python/gui/qgsrasterlayersaveasdialog.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class QgsRasterLayerSaveAsDialog: QDialog
3939
const QgsCoordinateReferenceSystem &currentCrs,
4040
QWidget *parent /TransferThis/ = 0,
4141
Qt::WindowFlags f = 0 );
42+
~QgsRasterLayerSaveAsDialog();
4243

4344
Mode mode() const;
4445
%Docstring

‎src/app/qgisapp.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6750,6 +6750,10 @@ void QgisApp::saveAsRasterFile( QgsRasterLayer *rasterLayer )
67506750
fileWriter.setMaxTileWidth( d.maximumTileSizeX() );
67516751
fileWriter.setMaxTileHeight( d.maximumTileSizeY() );
67526752
}
6753+
else
6754+
{
6755+
fileWriter.setOutputFormat( d.outputFormat() );
6756+
}
67536757

67546758
// TODO: show error dialogs
67556759
// TODO: this code should go somewhere else, but probably not into QgsRasterFileWriter

‎src/core/raster/qgsrasterfilewriter.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,3 +1009,17 @@ QString QgsRasterFileWriter::driverForExtension( const QString &extension )
10091009
}
10101010
return QString();
10111011
}
1012+
1013+
QStringList QgsRasterFileWriter::extensionsForFormat( const QString &format )
1014+
{
1015+
GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
1016+
if ( drv )
1017+
{
1018+
char **driverMetadata = GDALGetMetadata( drv, nullptr );
1019+
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1020+
{
1021+
return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1022+
}
1023+
}
1024+
return QStringList();
1025+
}

‎src/core/raster/qgsrasterfilewriter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ class CORE_EXPORT QgsRasterFileWriter
142142
*/
143143
static QString driverForExtension( const QString &extension );
144144

145+
/**
146+
* Returns a list of known file extensions for the given GDAL driver \a format.
147+
* E.g. returns "tif", "tiff" for the format "GTiff".
148+
*
149+
* If no matching format driver is found an empty list will be returned.
150+
*
151+
* \since QGIS 3.0
152+
*/
153+
static QStringList extensionsForFormat( const QString &format );
154+
145155
private:
146156
QgsRasterFileWriter(); //forbidden
147157
WriterError writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,

‎src/gui/qgsrasterlayersaveasdialog.cpp

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
#include "qgsrastertransparency.h"
2424
#include "qgsprojectionselectiondialog.h"
2525
#include "qgssettings.h"
26-
26+
#include "qgsrasterfilewriter.h"
27+
#include "cpl_string.h"
2728
#include <gdal.h>
2829

2930
#include <QFileDialog>
@@ -74,13 +75,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
7475

7576
toggleResolutionSize();
7677

77-
//only one hardcoded format at the moment
78-
QStringList myFormats;
79-
myFormats << QStringLiteral( "GTiff" );
80-
Q_FOREACH ( const QString &myFormat, myFormats )
81-
{
82-
mFormatComboBox->addItem( myFormat );
83-
}
78+
insertAvailableOutputFormats();
8479

8580
//fill reasonable default values depending on the provider
8681
if ( mDataProvider )
@@ -104,7 +99,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
10499
mCreateOptionsWidget->setProvider( mDataProvider->name() );
105100
if ( mDataProvider->name() == QLatin1String( "gdal" ) )
106101
{
107-
mCreateOptionsWidget->setFormat( myFormats[0] );
102+
mCreateOptionsWidget->setFormat( mFormatComboBox->currentData().toString() );
108103
}
109104
mCreateOptionsWidget->setRasterLayer( mRasterLayer );
110105
mCreateOptionsWidget->update();
@@ -163,6 +158,77 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
163158
connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsRasterLayerSaveAsDialog::extentChanged );
164159

165160
recalcResolutionSize();
161+
162+
QgsSettings settings;
163+
restoreGeometry( settings.value( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ) ).toByteArray() );
164+
}
165+
166+
QgsRasterLayerSaveAsDialog::~QgsRasterLayerSaveAsDialog()
167+
{
168+
QgsSettings settings;
169+
settings.setValue( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ), saveGeometry() );
170+
}
171+
172+
void QgsRasterLayerSaveAsDialog::insertAvailableOutputFormats()
173+
{
174+
GDALAllRegister();
175+
176+
int nDrivers = GDALGetDriverCount();
177+
QMap< int, QPair< QString, QString > > topPriorityDrivers;
178+
QMap< QString, QString > lowPriorityDrivers;
179+
180+
for ( int i = 0; i < nDrivers; ++i )
181+
{
182+
GDALDriverH driver = GDALGetDriver( i );
183+
if ( driver )
184+
{
185+
char **driverMetadata = GDALGetMetadata( driver, nullptr );
186+
187+
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
188+
{
189+
QString driverShortName = GDALGetDriverShortName( driver );
190+
QString driverLongName = GDALGetDriverLongName( driver );
191+
if ( driverShortName == QLatin1String( "MEM" ) )
192+
{
193+
// in memory rasters are not (yet) supported because the GDAL dataset handle
194+
// would need to be passed directly to QgsRasterLayer (it is not possible to
195+
// close it in raster calculator and reopen the dataset again in raster layer)
196+
continue;
197+
}
198+
else if ( driverShortName == QLatin1String( "VRT" ) )
199+
{
200+
// skip GDAL vrt driver, since we handle that format manually
201+
continue;
202+
}
203+
else if ( driverShortName == QStringLiteral( "GTiff" ) )
204+
{
205+
// always list geotiff first
206+
topPriorityDrivers.insert( 1, qMakePair( driverLongName, driverShortName ) );
207+
}
208+
else if ( driverShortName == QStringLiteral( "GPKG" ) )
209+
{
210+
// and gpkg second
211+
topPriorityDrivers.insert( 2, qMakePair( driverLongName, driverShortName ) );
212+
}
213+
else
214+
{
215+
lowPriorityDrivers.insert( driverLongName, driverShortName );
216+
}
217+
}
218+
}
219+
}
220+
221+
// will be sorted by priority, so that geotiff and geopackage are listed first
222+
for ( auto priorityDriversIt = topPriorityDrivers.constBegin(); priorityDriversIt != topPriorityDrivers.constEnd(); ++priorityDriversIt )
223+
{
224+
mFormatComboBox->addItem( priorityDriversIt.value().first, priorityDriversIt.value().second );
225+
}
226+
// will be sorted by driver name
227+
for ( auto lowPriorityDriversIt = lowPriorityDrivers.constBegin(); lowPriorityDriversIt != lowPriorityDrivers.constEnd(); ++lowPriorityDriversIt )
228+
{
229+
mFormatComboBox->addItem( lowPriorityDriversIt.key(), lowPriorityDriversIt.value() );
230+
}
231+
166232
}
167233

168234
void QgsRasterLayerSaveAsDialog::setValidators()
@@ -212,12 +278,26 @@ void QgsRasterLayerSaveAsDialog::mBrowseButton_clicked()
212278
}
213279
else
214280
{
215-
fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), dirName, tr( "GeoTIFF" ) + " (*.tif *.tiff *.TIF *.TIFF)" );
281+
QStringList extensions = QgsRasterFileWriter::extensionsForFormat( outputFormat() );
282+
QString filter;
283+
QString defaultExt;
284+
if ( extensions.empty() )
285+
filter = tr( "All files (*.*)" );
286+
else
287+
{
288+
filter = QStringLiteral( "%1 (*.%2);;%3" ).arg( mFormatComboBox->currentText(),
289+
extensions.join( QStringLiteral( " *." ) ),
290+
tr( "All files (*.*)" ) );
291+
defaultExt = extensions.at( 0 );
292+
}
293+
294+
fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), dirName, filter );
216295

217296
// ensure the user never omits the extension from the file name
218-
if ( !fileName.isEmpty() && !fileName.endsWith( QLatin1String( ".tif" ), Qt::CaseInsensitive ) && !fileName.endsWith( QLatin1String( ".tiff" ), Qt::CaseInsensitive ) )
297+
QFileInfo fi( fileName );
298+
if ( !fileName.isEmpty() && fi.suffix().isEmpty() )
219299
{
220-
fileName += QLatin1String( ".tif" );
300+
fileName += '.' + defaultExt;
221301
}
222302
}
223303

@@ -239,12 +319,12 @@ void QgsRasterLayerSaveAsDialog::mSaveAsLineEdit_textChanged( const QString &tex
239319
}
240320

241321

242-
void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QString &text )
322+
void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QString & )
243323
{
244324
//gdal-specific
245325
if ( mDataProvider && mDataProvider->name() == QLatin1String( "gdal" ) )
246326
{
247-
mCreateOptionsWidget->setFormat( text );
327+
mCreateOptionsWidget->setFormat( outputFormat() );
248328
mCreateOptionsWidget->update();
249329
}
250330
}
@@ -296,7 +376,7 @@ QString QgsRasterLayerSaveAsDialog::outputFileName() const
296376

297377
QString QgsRasterLayerSaveAsDialog::outputFormat() const
298378
{
299-
return mFormatComboBox->currentText();
379+
return mFormatComboBox->currentData().toString();
300380
}
301381

302382
QStringList QgsRasterLayerSaveAsDialog::createOptions() const

‎src/gui/qgsrasterlayersaveasdialog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
5858
const QgsCoordinateReferenceSystem &currentCrs,
5959
QWidget *parent SIP_TRANSFERTHIS = nullptr,
6060
Qt::WindowFlags f = 0 );
61+
~QgsRasterLayerSaveAsDialog();
6162

6263
Mode mode() const;
6364
int nColumns() const;
@@ -139,6 +140,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
139140
void adjustNoDataCellWidth( int row, int column );
140141
bool validate() const;
141142

143+
void insertAvailableOutputFormats();
142144
};
143145

144146

‎tests/src/python/test_qgsrasterfilewriter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ def testDriverForExtension(self):
109109
self.assertEqual(QgsRasterFileWriter.driverForExtension('not a format'), '')
110110
self.assertEqual(QgsRasterFileWriter.driverForExtension(''), '')
111111

112+
def testExtensionsForFormat(self):
113+
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('not format'), [])
114+
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GTiff'), ['tiff', 'tif'])
115+
self.assertCountEqual(QgsRasterFileWriter.extensionsForFormat('GPKG'), ['gpkg'])
116+
112117
def testImportIntoGpkg(self):
113118
# init target file
114119
test_gpkg = tempfile.mktemp(suffix='.gpkg', dir=self.testDataDir)

0 commit comments

Comments
 (0)
Please sign in to comment.