Skip to content

Commit c5cc634

Browse files
committedSep 20, 2012
raster save creation options validation:
- move help and validation to gdal provider - validate options before saving - test for valid PREDICTOR option, depending raster properties
1 parent bb7dc52 commit c5cc634

8 files changed

+203
-47
lines changed
 

‎python/gui/qgsrasterformatsaveoptionswidget.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class QgsRasterFormatSaveOptionsWidget : QWidget
2929

3030
void apply();
3131
void helpOptions();
32-
bool validateOptions( bool gui = true );
32+
QString validateOptions( bool gui = true, bool reportOk = true );
3333

3434
private slots:
3535

‎src/core/qgsrasterdataprovider.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,11 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
597597
tr( "Cubic" ) << tr( "Mode" ) << tr( "None" ) : QStringList();
598598
}
599599

600+
/** Validates creation options for a specific dataset and destination format - used by GDAL provider only.
601+
* See also validateCreationOptionsFormat() in gdal provider for validating options based on format only. */
602+
virtual QString validateCreationOptions( const QStringList& createOptions, QString format )
603+
{ Q_UNUSED( createOptions ); Q_UNUSED( format ); return QString(); }
604+
600605
signals:
601606
/** Emit a signal to notify of the progress event.
602607
* Emited theProgress is in percents (0.0-100.0) */

‎src/gui/qgsrasterformatsaveoptionswidget.cpp

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,8 @@
1818
#include "qgsrasterformatsaveoptionswidget.h"
1919
#include "qgslogger.h"
2020
#include "qgsdialog.h"
21-
22-
#include "gdal.h"
23-
#include "cpl_string.h"
24-
#include "cpl_conv.h"
25-
#include "cpl_minixml.h"
21+
#include "qgsrasterlayer.h"
22+
#include "qgsproviderregistry.h"
2623

2724
#include <QSettings>
2825
#include <QInputDialog>
@@ -31,28 +28,16 @@
3128
#include <QMouseEvent>
3229
#include <QMenu>
3330

34-
// todo put this somewhere else - how can we access gdal provider?
35-
char** papszFromStringList( const QStringList& list )
36-
{
37-
char **papszRetList = NULL;
38-
foreach ( QString elem, list )
39-
{
40-
papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
41-
}
42-
return papszRetList;
43-
}
4431

4532
QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::mBuiltinProfiles;
4633

4734
QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* parent, QString format,
48-
QgsRasterFormatSaveOptionsWidget::Type type,
49-
QString provider )
50-
: QWidget( parent ), mFormat( format ), mProvider( provider )
35+
QgsRasterFormatSaveOptionsWidget::Type type, QString provider )
36+
: QWidget( parent ), mFormat( format ), mProvider( provider ), mRasterLayer( 0 )
5137

5238
{
5339
setupUi( this );
5440

55-
5641
setType( type );
5742

5843
if ( mBuiltinProfiles.isEmpty() )
@@ -104,6 +89,7 @@ QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* par
10489
mOptionsLineEdit->installEventFilter( this );
10590
mOptionsStackedWidget->installEventFilter( this );
10691

92+
updateControls();
10793
updateProfiles();
10894
}
10995

@@ -114,12 +100,14 @@ QgsRasterFormatSaveOptionsWidget::~QgsRasterFormatSaveOptionsWidget()
114100
void QgsRasterFormatSaveOptionsWidget::setFormat( QString format )
115101
{
116102
mFormat = format;
103+
updateControls();
117104
updateProfiles();
118105
}
119106

120107
void QgsRasterFormatSaveOptionsWidget::setProvider( QString provider )
121108
{
122109
mProvider = provider;
110+
updateControls();
123111
}
124112

125113
// show/hide widgets - we need this function if widget is used in creator
@@ -236,27 +224,35 @@ void QgsRasterFormatSaveOptionsWidget::apply()
236224
setCreateOptions();
237225
}
238226

227+
// typedefs for gdal provider function pointers
228+
typedef QString validateCreationOptionsFormat_t( const QStringList& createOptions, QString format );
229+
typedef QString helpCreationOptionsFormat_t( QString format );
239230

240231
void QgsRasterFormatSaveOptionsWidget::helpOptions()
241232
{
242233
QString message;
243234

244235
if ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" )
245236
{
246-
GDALDriverH myGdalDriver = GDALGetDriverByName( mFormat.toLocal8Bit().constData() );
247-
if ( myGdalDriver )
237+
// get helpCreationOptionsFormat() function ptr for provider
238+
QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( mProvider );
239+
if ( library )
248240
{
249-
// need to serialize xml to get newlines
250-
CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
251-
GDAL_DMD_CREATIONOPTIONLIST, "" ) );
252-
char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
253-
if ( pszFormattedXML )
254-
message = tr( "Create Options:\n\n%1" ).arg( pszFormattedXML );
255-
if ( psCOL )
256-
CPLDestroyXMLNode( psCOL );
257-
if ( pszFormattedXML )
258-
CPLFree( pszFormattedXML );
241+
helpCreationOptionsFormat_t * helpCreationOptionsFormat =
242+
( helpCreationOptionsFormat_t * ) cast_to_fptr( library->resolve( "helpCreationOptionsFormat" ) );
243+
if ( helpCreationOptionsFormat )
244+
{
245+
message = helpCreationOptionsFormat( mFormat );
246+
}
247+
else
248+
{
249+
message = library->fileName() + " does not have helpCreationOptionsFormat";
250+
}
259251
}
252+
else
253+
message = QString( "cannot load provider library %1" ).arg( mProvider );
254+
255+
260256
if ( message.isEmpty() )
261257
message = tr( "Cannot get create options for driver %1" ).arg( mFormat );
262258
}
@@ -267,36 +263,65 @@ void QgsRasterFormatSaveOptionsWidget::helpOptions()
267263
QgsDialog *dlg = new QgsDialog( this );
268264
QTextEdit *textEdit = new QTextEdit( dlg );
269265
textEdit->setReadOnly( true );
266+
message = tr( "Create Options:\n\n%1" ).arg( message );
270267
textEdit->setText( message );
271268
dlg->layout()->addWidget( textEdit );
272-
dlg->resize( 600, 600 );
269+
dlg->resize( 600, 400 );
273270
dlg->show(); //non modal
274271
}
275272

276-
bool QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui )
273+
QString QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui, bool reportOK )
277274
{
278275
QStringList createOptions = options();
279-
bool ok = false;
276+
QString message;
280277

281278
if ( !createOptions.isEmpty() && mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" )
282279
{
283-
GDALDriverH myGdalDriver = GDALGetDriverByName( mFormat.toLocal8Bit().constData() );
284-
if ( myGdalDriver )
280+
if ( mRasterLayer )
281+
{
282+
QgsDebugMsg( "calling validate on layer's data provider" );
283+
message = mRasterLayer->dataProvider()->validateCreationOptions( createOptions, mFormat );
284+
}
285+
else
285286
{
286-
// print error string?
287-
char** papszOptions = papszFromStringList( createOptions );
288-
ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
289-
CSLDestroy( papszOptions );
290-
if ( gui )
287+
// get validateCreationOptionsFormat() function ptr for provider
288+
QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( mProvider );
289+
if ( library )
291290
{
292-
if ( ok )
293-
QMessageBox::information( this, "", tr( "Valid" ), QMessageBox::Close );
291+
validateCreationOptionsFormat_t * validateCreationOptionsFormat =
292+
( validateCreationOptionsFormat_t * ) cast_to_fptr( library->resolve( "validateCreationOptionsFormat" ) );
293+
if ( validateCreationOptionsFormat )
294+
{
295+
message = validateCreationOptionsFormat( createOptions, mFormat );
296+
}
294297
else
295-
QMessageBox::warning( this, "", tr( "Invalid" ), QMessageBox::Close );
298+
{
299+
message = library->fileName() + " does not have validateCreationOptionsFormat";
300+
}
301+
}
302+
else
303+
message = QString( "cannot load provider library %1" ).arg( mProvider );
304+
}
305+
306+
if ( gui )
307+
{
308+
if ( message.isNull() )
309+
{
310+
if ( reportOK )
311+
QMessageBox::information( this, "", tr( "Valid" ), QMessageBox::Close );
312+
}
313+
else
314+
{
315+
QMessageBox::warning( this, "", tr( "Invalid creation option :\n\n%1\n\nClick on help button to get valid creation options for this format" ).arg( message ), QMessageBox::Close );
296316
}
297317
}
298318
}
299-
return ok;
319+
else
320+
{
321+
QMessageBox::information( this, "", tr( "Cannot validate" ), QMessageBox::Close );
322+
}
323+
324+
return message;
300325
}
301326

302327
void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
@@ -481,6 +506,13 @@ void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( int newIndex )
481506
updateOptions();
482507
}
483508

509+
void QgsRasterFormatSaveOptionsWidget::updateControls()
510+
{
511+
bool enabled = ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" );
512+
mOptionsValidateButton->setEnabled( enabled );
513+
mOptionsHelpButton->setEnabled( enabled );
514+
}
515+
484516
// map options label left mouse click to optionsToggle()
485517
bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
486518
{

‎src/gui/qgsrasterformatsaveoptionswidget.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#include "ui_qgsrasterformatsaveoptionswidgetbase.h"
2222

23+
class QgsRasterLayer;
24+
2325
/** \ingroup gui
2426
* A widget to select format-specific raster saving options
2527
*/
@@ -46,14 +48,15 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget,
4648

4749
void setFormat( QString format );
4850
void setProvider( QString provider );
51+
void setRasterLayer( QgsRasterLayer* rasterLayer ) { mRasterLayer = rasterLayer; }
4952
QStringList options() const;
5053
void setType( QgsRasterFormatSaveOptionsWidget::Type type = Default );
5154

5255
public slots:
5356

5457
void apply();
5558
void helpOptions();
56-
bool validateOptions( bool gui = true );
59+
QString validateOptions( bool gui = true, bool reportOk = true );
5760
void updateProfiles();
5861

5962
private slots:
@@ -68,11 +71,13 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget,
6871
void optionsTableEnableDeleteButton();
6972
void updateOptions();
7073
void swapOptionsUI( int newIndex = -1 );
74+
void updateControls();
7175

7276
private:
7377

7478
QString mFormat;
7579
QString mProvider;
80+
QgsRasterLayer* mRasterLayer;
7681
QMap< QString, QString> mOptionsMap;
7782
static QMap< QString, QStringList > mBuiltinProfiles;
7883

‎src/gui/qgsrasterlayersaveasdialog.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer* rasterLa
7676
{
7777
mCreateOptionsWidget->setFormat( myFormats[0] );
7878
}
79+
mCreateOptionsWidget->setRasterLayer( mRasterLayer );
7980
mCreateOptionsWidget->update();
8081
}
8182

@@ -753,3 +754,14 @@ QgsRasterDataProvider::RasterBuildPyramids QgsRasterLayerSaveAsDialog::buildPyra
753754
return QgsRasterDataProvider::PyramidsFlagYes;
754755
}
755756

757+
bool QgsRasterLayerSaveAsDialog::validate() const
758+
{
759+
if ( mCreateOptionsGroupBox->isChecked() )
760+
{
761+
QString message = mCreateOptionsWidget->validateOptions( true, false );
762+
if ( !message.isNull() )
763+
return false;
764+
}
765+
return true;
766+
}
767+

‎src/gui/qgsrasterlayersaveasdialog.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
6767
void hideFormat();
6868
void hideOutput();
6969

70+
public slots:
71+
virtual void accept() { if ( validate() ) return QDialog::accept(); }
72+
7073
private slots:
7174
void on_mRawModeRadioButton_toggled( bool );
7275
void on_mBrowseButton_clicked();
@@ -132,6 +135,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
132135
void setNoDataToEdited( int row );
133136
double noDataCellValue( int row, int column ) const;
134137
void adjustNoDataCellWidth( int row, int column );
138+
bool validate() const;
135139
};
136140

137141

‎src/providers/gdal/qgsgdalprovider.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2458,3 +2458,99 @@ QGISEXTERN void buildSupportedRasterFileFilter( QString & theFileFiltersString )
24582458
buildSupportedRasterFileFilterAndExtensions( theFileFiltersString, exts, wildcards );
24592459
}
24602460

2461+
/**
2462+
Gets creation options metadata for a given format
2463+
*/
2464+
QGISEXTERN QString helpCreationOptionsFormat( QString format )
2465+
{
2466+
QString message;
2467+
GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
2468+
if ( myGdalDriver )
2469+
{
2470+
// need to serialize xml to get newlines
2471+
// should we make the basic xml prettier?
2472+
CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
2473+
GDAL_DMD_CREATIONOPTIONLIST, "" ) );
2474+
char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
2475+
if ( pszFormattedXML )
2476+
message = QString( pszFormattedXML );
2477+
if ( psCOL )
2478+
CPLDestroyXMLNode( psCOL );
2479+
if ( pszFormattedXML )
2480+
CPLFree( pszFormattedXML );
2481+
}
2482+
return message;
2483+
}
2484+
2485+
/**
2486+
Validates creation options for a given format, regardless of layer.
2487+
*/
2488+
QGISEXTERN QString validateCreationOptionsFormat( const QStringList& createOptions, QString format )
2489+
{
2490+
GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
2491+
if ( ! myGdalDriver )
2492+
return "invalid GDAL driver";
2493+
2494+
char** papszOptions = papszFromStringList( createOptions );
2495+
// get error string?
2496+
int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
2497+
CSLDestroy( papszOptions );
2498+
2499+
if ( ok == FALSE )
2500+
return "Failed GDALValidateCreationOptions() test";
2501+
return QString();
2502+
}
2503+
2504+
QString QgsGdalProvider::validateCreationOptions( const QStringList& createOptions, QString format )
2505+
{
2506+
QString message;
2507+
2508+
// first validate basic syntax with GDALValidateCreationOptions
2509+
message = validateCreationOptionsFormat( createOptions, format );
2510+
if ( !message.isNull() )
2511+
return message;
2512+
2513+
// next do specific validations, depending on format and dataset
2514+
// only check certain destination formats
2515+
QStringList formatsCheck;
2516+
formatsCheck << "gtiff";
2517+
if ( ! formatsCheck.contains( format.toLower() ) )
2518+
return QString();
2519+
2520+
// prepare a map for easier lookup
2521+
QMap< QString, QString > optionsMap;
2522+
foreach ( QString option, createOptions )
2523+
{
2524+
QStringList opt = option.split( "=" );
2525+
optionsMap[ opt[0].toUpper()] = opt[1];
2526+
QgsDebugMsg( "option: " + option );
2527+
}
2528+
2529+
// gtiff files - validate PREDICTOR option
2530+
// see gdal: frmts/gtiff/geotiff.cpp and libtiff: tif_predict.c)
2531+
if ( format.toLower() == "gtiff" && optionsMap.contains( "PREDICTOR" ) )
2532+
{
2533+
QString value = optionsMap.value( "PREDICTOR" );
2534+
GDALDataType nDataType = ( mGdalDataType.count() > 0 ) ? ( GDALDataType ) mGdalDataType[ 0 ] : GDT_Unknown;
2535+
int nBitsPerSample = nDataType != GDT_Unknown ? GDALGetDataTypeSize( nDataType ) : 0;
2536+
QgsDebugMsg( QString( "PREDICTOR: %1 nbits: %2 type: %3" ).arg( value ).arg( nBitsPerSample ).arg(( GDALDataType ) mGdalDataType[ 0 ] ) );
2537+
// PREDICTOR=2 only valid for 8/16/32 bits per sample
2538+
// TODO check for NBITS option (see geotiff.cpp)
2539+
if ( value == "2" )
2540+
{
2541+
if ( nBitsPerSample != 8 && nBitsPerSample != 16 &&
2542+
nBitsPerSample != 32 )
2543+
{
2544+
message = QString( "PREDICTOR=%1 only valid for 8/16/32 bits per sample (using %2)" ).arg( value ).arg( nBitsPerSample );
2545+
}
2546+
}
2547+
// PREDICTOR=3 only valid for float/double precision
2548+
else if ( value == "3" )
2549+
{
2550+
if ( nDataType != GDT_Float32 && nDataType != GDT_Float64 )
2551+
message = "PREDICTOR=3 only valid for float/double precision";
2552+
}
2553+
}
2554+
2555+
return message;
2556+
}

‎src/providers/gdal/qgsgdalprovider.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
285285
/**Remove dataset*/
286286
bool remove();
287287

288+
QString validateCreationOptions( const QStringList& createOptions, QString format );
289+
288290
signals:
289291
void statusChanged( QString );
290292

0 commit comments

Comments
 (0)
Please sign in to comment.