diff --git a/python/core/raster/qgsrasterdataprovider.sip b/python/core/raster/qgsrasterdataprovider.sip index 7ffac6ed7a3..4b400d9dad3 100644 --- a/python/core/raster/qgsrasterdataprovider.sip +++ b/python/core/raster/qgsrasterdataprovider.sip @@ -169,10 +169,11 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface virtual QStringList subLayers() const; /** \brief Create pyramid overviews */ - virtual QString buildPyramids( const QList & thePyramidList, - const QString & theResamplingMethod = "NEAREST", - RasterPyramidsFormat theFormat = PyramidsGTiff ); - + virtual QString buildPyramids( const QList & thePyramidList, + const QString & theResamplingMethod = "NEAREST", + RasterPyramidsFormat theFormat = PyramidsGTiff, + const QStringList & theConfigOptions = QStringList() ); + /** \brief Accessor for ths raster layers pyramid list. * @param overviewList used to construct the pyramid list (optional), when empty the list is defined by the provider. * A pyramid list defines the @@ -269,13 +270,22 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface /** Remove dataset*/ virtual bool remove(); - static QStringList pyramidResamplingMethods( QString providerKey ); + /** Returns a list of pyramid resampling method names for given provider */ + static QStringList pyramidResamplingMethods( QString providerKey = "gdal" ); + /** Returns the pyramid resampling argument that corresponds to a given method */ + static QString pyramidResamplingArg( QString method, QString providerKey = "gdal" ); - /** Validates creation options for a specific dataset and destination format - used by GDAL provider only. - * See also validateCreationOptionsFormat() in gdal provider for validating options based on format only. */ - virtual QString validateCreationOptions( const QStringList& createOptions, QString format ); + /** Validates creation options for a specific dataset and destination format. + * @note used by GDAL provider only + * @note see also validateCreationOptionsFormat() in gdal provider for validating options based on format only */ + virtual QString validateCreationOptions( const QStringList& createOptions, QString format ); - signals: + /** Validates pyramid creation options for a specific dataset and destination format + * @note used by GDAL provider only */ + virtual QString validatePyramidsConfigOptions( RasterPyramidsFormat pyramidsFormat, + const QStringList & theConfigOptions, const QString & fileFormat ); + + signals: /** Emit a signal to notify of the progress event. * Emited theProgress is in percents (0.0-100.0) */ void progress( int theType, double theProgress, QString theMessage ); diff --git a/python/core/raster/qgsrasterfilewriter.sip b/python/core/raster/qgsrasterfilewriter.sip index 45f82a05667..1f96567e544 100644 --- a/python/core/raster/qgsrasterfilewriter.sip +++ b/python/core/raster/qgsrasterfilewriter.sip @@ -59,8 +59,10 @@ class QgsRasterFileWriter void setMaxTileHeight( int h ); int maxTileHeight() const; - // for now not putting createOptions in all methods, use createOptions() void setCreateOptions( const QStringList& list ); QStringList createOptions() const; + + QStringList pyramidsConfigOptions() const; + void setPyramidsConfigOptions( const QStringList& list ); }; diff --git a/python/gui/qgsrasterformatsaveoptionswidget.sip b/python/gui/qgsrasterformatsaveoptionswidget.sip index 3fb6bdeac67..89ccebdce02 100644 --- a/python/gui/qgsrasterformatsaveoptionswidget.sip +++ b/python/gui/qgsrasterformatsaveoptionswidget.sip @@ -24,8 +24,10 @@ class QgsRasterFormatSaveOptionsWidget : QWidget void setFormat( QString format ); void setProvider( QString provider ); void setRasterLayer( QgsRasterLayer* rasterLayer ); + void setRasterFileName( const QString& file ); QStringList options() const; void setType( QgsRasterFormatSaveOptionsWidget::Type type = Default ); + void setPyramidsFormat( QgsRasterDataProvider::RasterPyramidsFormat format ); public slots: diff --git a/python/gui/qgsrasterlayersaveasdialog.sip b/python/gui/qgsrasterlayersaveasdialog.sip index 5b0f69ea132..06735729164 100644 --- a/python/gui/qgsrasterlayersaveasdialog.sip +++ b/python/gui/qgsrasterlayersaveasdialog.sip @@ -48,10 +48,11 @@ class QgsRasterLayerSaveAsDialog : QDialog QgsRectangle outputRectangle() const; QList noData() const; - QList< int > overviewList() const; + QList< int > pyramidsList() const; QgsRasterDataProvider::RasterBuildPyramids buildPyramidsFlag() const; - QString pyramidsResampling() const; + QString pyramidsResamplingMethod() const; QgsRasterDataProvider::RasterPyramidsFormat pyramidsFormat() const; + QStringList pyramidsConfigOptions() const; void hideFormat(); void hideOutput(); diff --git a/python/gui/qgsrasterpyramidsoptionswidget.sip b/python/gui/qgsrasterpyramidsoptionswidget.sip index 5505c5f488c..8eeea6c6c9e 100644 --- a/python/gui/qgsrasterpyramidsoptionswidget.sip +++ b/python/gui/qgsrasterpyramidsoptionswidget.sip @@ -12,17 +12,26 @@ class QgsRasterPyramidsOptionsWidget: QWidget QgsRasterPyramidsOptionsWidget( QWidget* parent = 0, QString provider = "gdal" ); ~QgsRasterPyramidsOptionsWidget(); - QStringList createOptions() const; + QStringList configOptions() const; QgsRasterFormatSaveOptionsWidget* createOptionsWidget() /Factory/; const QList overviewList() const; QgsRasterDataProvider::RasterPyramidsFormat pyramidsFormat() const; QString resamplingMethod() const; + void setRasterLayer( QgsRasterLayer* rasterLayer ); + void setRasterFileName( const QString& file ); public slots: void apply(); void checkAllLevels( bool checked ); + private slots: + + void on_cbxPyramidsLevelsCustom_toggled( bool toggled ); + void on_cbxPyramidsFormat_currentIndexChanged( int index ); + void setOverviewList(); + void updateUi(); + signals: void overviewListChanged(); }; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index a8002015f70..9319751caa0 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -4204,9 +4204,10 @@ void QgisApp::saveAsRasterFile() fileWriter.setCreateOptions( d.createOptions() ); fileWriter.setBuildPyramidsFlag( d.buildPyramidsFlag() ); - fileWriter.setPyramidsList( d.overviewList() ); - fileWriter.setPyramidsResampling( d.pyramidsResampling() ); + fileWriter.setPyramidsList( d.pyramidsList() ); + fileWriter.setPyramidsResampling( d.pyramidsResamplingMethod() ); fileWriter.setPyramidsFormat( d.pyramidsFormat() ); + fileWriter.setPyramidsConfigOptions( d.pyramidsConfigOptions() ); QgsRasterFileWriter::WriterError err = fileWriter.writeRaster( pipe, d.nColumns(), d.nRows(), d.outputRectangle(), d.outputCrs(), &pd ); if ( err != QgsRasterFileWriter::NoError ) diff --git a/src/core/qgis.h b/src/core/qgis.h index dcf82d16052..7e8fb7f6bc9 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -336,6 +336,8 @@ const double DEFAULT_LINE_WIDTH = 0.26; /** default snapping tolerance for segments (@note added in 1.8) */ const double DEFAULT_SEGMENT_EPSILON = 1e-8; +typedef QMap QgsStringMap; + // FIXME: also in qgisinterface.h #ifndef QGISEXTERN #ifdef WIN32 diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index 6c682e0686a..47b74904b4e 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -387,6 +387,63 @@ QString QgsRasterDataProvider::lastErrorFormat() return "text/plain"; } +// pyramids resampling + +// TODO move this to gdal provider +// but we need some way to get a static instance of the provider +// or use function pointers like in QgsRasterFormatSaveOptionsWidget::helpOptions() + +// see http://www.gdal.org/gdaladdo.html +// http://www.gdal.org/classGDALDataset.html#a2aa6f88b3bbc840a5696236af11dde15 +// http://www.gdal.org/classGDALRasterBand.html#afaea945b13ec9c86c2d783b883c68432 + +// from http://www.gdal.org/gdaladdo.html +// average_mp is unsuitable for use thus not included + +// from qgsgdalprovider.cpp (removed) +// NOTE magphase is disabled in the gui since it tends +// to create corrupted images. The images can be repaired +// by running one of the other resampling strategies below. +// see ticket #284 +QStringList QgsRasterDataProvider::mPyramidResamplingListGdal = QStringList(); +QgsStringMap QgsRasterDataProvider::mPyramidResamplingMapGdal = QgsStringMap(); + +void QgsRasterDataProvider::initPyramidResamplingDefs() +{ + mPyramidResamplingListGdal.clear(); + mPyramidResamplingListGdal << tr( "Nearest Neighbour" ) << tr( "Average" ) << tr( "Gauss" ) << tr( "Cubic" ) << tr( "Mode" ) << tr( "None" ); // << tr( "Average magphase" ) + mPyramidResamplingMapGdal.clear(); + mPyramidResamplingMapGdal[ tr( "Nearest Neighbour" )] = "NEAREST"; + mPyramidResamplingMapGdal[ tr( "Average" )] = "AVERAGE"; + mPyramidResamplingMapGdal[ tr( "Gauss" )] = "GAUSS"; + mPyramidResamplingMapGdal[ tr( "Cubic" )] = "CUBIC"; + mPyramidResamplingMapGdal[ tr( "Mode" )] = "MODE"; + // mPyramidResamplingMapGdal[ tr( "Average magphase" ) ] = "average_magphase"; + mPyramidResamplingMapGdal[ tr( "None" )] = "NONE" ; +} + +QStringList QgsRasterDataProvider::pyramidResamplingMethods( QString providerKey ) +{ + if ( mPyramidResamplingListGdal.isEmpty() ) + initPyramidResamplingDefs(); + + return providerKey == "gdal" ? mPyramidResamplingListGdal : QStringList(); +} + +QString QgsRasterDataProvider::pyramidResamplingArg( QString method, QString providerKey ) +{ + if ( providerKey != "gdal" ) + return QString(); + + if ( mPyramidResamplingListGdal.isEmpty() ) + initPyramidResamplingDefs(); + + if ( mPyramidResamplingMapGdal.contains( method ) ) + return mPyramidResamplingMapGdal.value( method ); + else + return "NEAREST"; +} + bool QgsRasterDataProvider::hasPyramids() { QList myPyramidList = buildPyramidList(); diff --git a/src/core/raster/qgsrasterdataprovider.h b/src/core/raster/qgsrasterdataprovider.h index 2791d4cc5b6..d235c8e552d 100644 --- a/src/core/raster/qgsrasterdataprovider.h +++ b/src/core/raster/qgsrasterdataprovider.h @@ -290,11 +290,13 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast } /** \brief Create pyramid overviews */ - virtual QString buildPyramids( const QList & thePyramidList, - const QString & theResamplingMethod = "NEAREST", - RasterPyramidsFormat theFormat = PyramidsGTiff ) + virtual QString buildPyramids( const QList & thePyramidList, + const QString & theResamplingMethod = "NEAREST", + RasterPyramidsFormat theFormat = PyramidsGTiff, + const QStringList & theConfigOptions = QStringList() ) { - Q_UNUSED( thePyramidList ); Q_UNUSED( theResamplingMethod ); Q_UNUSED( theFormat ); + Q_UNUSED( thePyramidList ); Q_UNUSED( theResamplingMethod ); + Q_UNUSED( theFormat ); Q_UNUSED( theConfigOptions ); return "FAILED_NOT_SUPPORTED"; }; @@ -443,18 +445,23 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast /** Remove dataset*/ virtual bool remove() { return false; } - static QStringList pyramidResamplingMethods( QString providerKey ) - { - return providerKey == "gdal" ? - QStringList() << tr( "Average" ) << tr( "Nearest Neighbour" ) << tr( "Gauss" ) << - tr( "Cubic" ) << tr( "Mode" ) << tr( "None" ) : QStringList(); - } + /** Returns a list of pyramid resampling method names for given provider */ + static QStringList pyramidResamplingMethods( QString providerKey = "gdal" ); + /** Returns the pyramid resampling argument that corresponds to a given method */ + static QString pyramidResamplingArg( QString method, QString providerKey = "gdal" ); - /** Validates creation options for a specific dataset and destination format - used by GDAL provider only. - * See also validateCreationOptionsFormat() in gdal provider for validating options based on format only. */ + /** Validates creation options for a specific dataset and destination format. + * @note used by GDAL provider only + * @note see also validateCreationOptionsFormat() in gdal provider for validating options based on format only */ virtual QString validateCreationOptions( const QStringList& createOptions, QString format ) { Q_UNUSED( createOptions ); Q_UNUSED( format ); return QString(); } + /** Validates pyramid creation options for a specific dataset and destination format + * @note used by GDAL provider only */ + virtual QString validatePyramidsConfigOptions( RasterPyramidsFormat pyramidsFormat, + const QStringList & theConfigOptions, const QString & fileFormat ) + { Q_UNUSED( pyramidsFormat ); Q_UNUSED( theConfigOptions ); Q_UNUSED( fileFormat ); return QString(); } + signals: /** Emit a signal to notify of the progress event. * Emited theProgress is in percents (0.0-100.0) */ @@ -496,5 +503,9 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast QgsRectangle mExtent; + static void initPyramidResamplingDefs(); + static QStringList mPyramidResamplingListGdal; + static QgsStringMap mPyramidResamplingMapGdal; + }; #endif diff --git a/src/core/raster/qgsrasterfilewriter.cpp b/src/core/raster/qgsrasterfilewriter.cpp index a97dc8a1861..e1b06347ed2 100644 --- a/src/core/raster/qgsrasterfilewriter.cpp +++ b/src/core/raster/qgsrasterfilewriter.cpp @@ -717,9 +717,10 @@ void QgsRasterFileWriter::buildPyramids( const QString& filename ) myPyramidList[myCounterInt].build = true; } - QgsDebugMsg( QString( "building pyramids : %1 pyramids, %2 resampling, %3 format" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ) ); + QgsDebugMsg( QString( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ) ); // QApplication::setOverrideCursor( Qt::WaitCursor ); - QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling, mPyramidsFormat ); + QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling, + mPyramidsFormat, mPyramidsConfigOptions ); // QApplication::restoreOverrideCursor(); // TODO put this in provider or elsewhere diff --git a/src/core/raster/qgsrasterfilewriter.h b/src/core/raster/qgsrasterfilewriter.h index fe58d05602d..25bff000147 100644 --- a/src/core/raster/qgsrasterfilewriter.h +++ b/src/core/raster/qgsrasterfilewriter.h @@ -88,10 +88,12 @@ class CORE_EXPORT QgsRasterFileWriter void setMaxTileHeight( int h ) { mMaxTileHeight = h; } int maxTileHeight() const { return mMaxTileHeight; } - // for now not putting createOptions in all methods, use createOptions() void setCreateOptions( const QStringList& list ) { mCreateOptions = list; } QStringList createOptions() const { return mCreateOptions; } + void setPyramidsConfigOptions( const QStringList& list ) { mPyramidsConfigOptions = list; } + QStringList pyramidsConfigOptions() const { return mPyramidsConfigOptions; } + private: QgsRasterFileWriter(); //forbidden //WriterError writeDataRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent, @@ -157,6 +159,7 @@ class CORE_EXPORT QgsRasterFileWriter QString mPyramidsResampling; QgsRasterDataProvider::RasterBuildPyramids mBuildPyramidsFlag; QgsRasterDataProvider::RasterPyramidsFormat mPyramidsFormat; + QStringList mPyramidsConfigOptions; QDomDocument mVRTDocument; QList mVRTBands; diff --git a/src/core/symbology-ng/qgssymbolv2.h b/src/core/symbology-ng/qgssymbolv2.h index 0f3b07bc63d..8724df2966d 100644 --- a/src/core/symbology-ng/qgssymbolv2.h +++ b/src/core/symbology-ng/qgssymbolv2.h @@ -36,7 +36,6 @@ class QgsSymbolLayerV2; class QgsRenderContext; class QgsVectorLayer; -typedef QMap QgsStringMap; typedef QList QgsSymbolLayerV2List; class CORE_EXPORT QgsSymbolV2 diff --git a/src/gui/qgsrasterformatsaveoptionswidget.cpp b/src/gui/qgsrasterformatsaveoptionswidget.cpp index d0e4b641e59..c044b189ce5 100644 --- a/src/gui/qgsrasterformatsaveoptionswidget.cpp +++ b/src/gui/qgsrasterformatsaveoptionswidget.cpp @@ -33,7 +33,9 @@ QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::mBuiltinProfiles; QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* parent, QString format, QgsRasterFormatSaveOptionsWidget::Type type, QString provider ) - : QWidget( parent ), mFormat( format ), mProvider( provider ), mRasterLayer( 0 ) + : QWidget( parent ), mFormat( format ), mProvider( provider ), mRasterLayer( 0 ), + mRasterFileName( QString() ), mPyramids( false ), + mPyramidsFormat( QgsRasterDataProvider::PyramidsGTiff ) { setupUi( this ); @@ -58,8 +60,8 @@ QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* par ( QStringList() << "GTiff" << tr( "High compression" ) << "COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ); mBuiltinProfiles[ "z_gtiff_4jpeg" ] = - ( QStringList() << "GTiff" << tr( "Lossy compression" ) - << "COMPRESS=JPEG" ); + ( QStringList() << "GTiff" << tr( "JPEG compression" ) + << "COMPRESS=JPEG JPEG_QUALITY=75" ); // overview compression schemes for GTiff format, see // http://www.gdal.org/gdaladdo.html and http://www.gdal.org/frmt_gtiff.html @@ -74,8 +76,8 @@ QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* par ( QStringList() << "_pyramids" << tr( "High compression" ) << "COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ); // how to set zlevel? mBuiltinProfiles[ "z__pyramids_gtiff_4jpeg" ] = - ( QStringList() << "_pyramids" << tr( "Lossy compression" ) - << "COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ); + ( QStringList() << "_pyramids" << tr( "JPEG compression" ) + << "JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ); } connect( mProfileComboBox, SIGNAL( currentIndexChanged( const QString & ) ), @@ -91,6 +93,8 @@ QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* par updateControls(); updateProfiles(); + + QgsDebugMsg( "done" ); } QgsRasterFormatSaveOptionsWidget::~QgsRasterFormatSaveOptionsWidget() @@ -123,7 +127,7 @@ void QgsRasterFormatSaveOptionsWidget::setType( QgsRasterFormatSaveOptionsWidget foreach ( QWidget* widget, mOptionsStackedWidget->findChildren() ) widget->setVisible( true ); - // show elevant page + // show relevant page if ( type == Table ) swapOptionsUI( 0 ); else if ( type == LineEdit ) @@ -146,17 +150,20 @@ void QgsRasterFormatSaveOptionsWidget::setType( QgsRasterFormatSaveOptionsWidget void QgsRasterFormatSaveOptionsWidget::updateProfiles() { // build profiles list = user + builtin(last) + QString format = mPyramids ? "_pyramids" : mFormat; QStringList profileKeys = profiles(); QMapIterator it( mBuiltinProfiles ); while ( it.hasNext() ) { it.next(); QString profileKey = it.key(); - if ( ! profileKeys.contains( profileKey ) ) + if ( ! profileKeys.contains( profileKey ) && it.value().count() > 0 ) { // insert key if is for all formats or this format (GTiff) - if ( it.value()[0] == "" || it.value()[0] == mFormat ) + if ( it.value()[0] == "" || it.value()[0] == format ) + { profileKeys.insert( 0, profileKey ); + } } } qSort( profileKeys ); @@ -188,7 +195,7 @@ void QgsRasterFormatSaveOptionsWidget::updateProfiles() // mProfileComboBox->setCurrentIndex( 0 ); QSettings mySettings; mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.value( - mProvider + "/driverOptions/" + mFormat.toLower() + "/defaultProfile", + mProvider + "/driverOptions/" + format.toLower() + "/defaultProfile", "z_adefault" ) ) ); updateOptions(); } @@ -217,6 +224,8 @@ void QgsRasterFormatSaveOptionsWidget::updateOptions() mOptionsLineEdit->setText( myOptions ); mOptionsLineEdit->setCursorPosition( 0 ); } + + emit optionsChanged(); } void QgsRasterFormatSaveOptionsWidget::apply() @@ -232,7 +241,7 @@ void QgsRasterFormatSaveOptionsWidget::helpOptions() { QString message; - if ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" ) + if ( mProvider == "gdal" && mFormat != "" && ! mPyramids ) { // get helpCreationOptionsFormat() function ptr for provider QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( mProvider ); @@ -256,6 +265,11 @@ void QgsRasterFormatSaveOptionsWidget::helpOptions() if ( message.isEmpty() ) message = tr( "Cannot get create options for driver %1" ).arg( mFormat ); } + else if ( mProvider == "gdal" && mPyramids ) + { + message = tr( "For details on pyramids options please see the following pages" ); + message += "\n\nhttp://www.gdal.org/gdaladdo.html\n\nhttp://www.gdal.org/frmt_gtiff.html"; + } else message = tr( "No help available" ); @@ -263,7 +277,7 @@ void QgsRasterFormatSaveOptionsWidget::helpOptions() QgsDialog *dlg = new QgsDialog( this ); QTextEdit *textEdit = new QTextEdit( dlg ); textEdit->setReadOnly( true ); - message = tr( "Create Options:\n\n%1" ).arg( message ); + // message = tr( "Create Options:\n\n%1" ).arg( message ); textEdit->setText( message ); dlg->layout()->addWidget( textEdit ); dlg->resize( 600, 400 ); @@ -275,12 +289,49 @@ QString QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui, bool report QStringList createOptions = options(); QString message; - if ( !createOptions.isEmpty() && mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" ) + QgsDebugMsg( QString( "layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->id() : "none" ).arg( mRasterFileName ).arg( mFormat ) ); + // if no rasterLayer is defined, but we have a raster fileName, then create a temp. rasterLayer to validate options + // ideally we should keep it for future access, but this is trickier + QgsRasterLayer* rasterLayer = mRasterLayer; + bool tmpLayer = false; + if ( !( mRasterLayer && rasterLayer->dataProvider() ) && ! mRasterFileName.isNull() ) { - if ( mRasterLayer ) + // temporarily override /Projections/defaultBehaviour to avoid dialog prompt + // this is taken from qgsbrowserdockwidget.cpp + // TODO - integrate this into qgis core + QSettings settings; + QString defaultProjectionOption = settings.value( "/Projections/defaultBehaviour", "prompt" ).toString(); + if ( settings.value( "/Projections/defaultBehaviour", "prompt" ).toString() == "prompt" ) + { + settings.setValue( "/Projections/defaultBehaviour", "useProject" ); + } + tmpLayer = true; + rasterLayer = new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), "gdal" ); + // restore /Projections/defaultBehaviour + if ( defaultProjectionOption == "prompt" ) + { + settings.setValue( "/Projections/defaultBehaviour", defaultProjectionOption ); + } + } + + if ( mProvider == "gdal" && mPyramids ) + { + if ( rasterLayer && rasterLayer->dataProvider() ) + { + QgsDebugMsg( "calling validate pyramids on layer's data provider" ); + message = rasterLayer->dataProvider()->validatePyramidsConfigOptions( mPyramidsFormat, createOptions, mFormat ); + } + else + { + message = tr( "cannot validate pyramid options" ); + } + } + else if ( !createOptions.isEmpty() && mProvider == "gdal" && mFormat != "" ) + { + if ( rasterLayer && rasterLayer->dataProvider() ) { QgsDebugMsg( "calling validate on layer's data provider" ); - message = mRasterLayer->dataProvider()->validateCreationOptions( createOptions, mFormat ); + message = rasterLayer->dataProvider()->validateCreationOptions( createOptions, mFormat ); } else { @@ -302,24 +353,30 @@ QString QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui, bool report else message = QString( "cannot load provider library %1" ).arg( mProvider ); } + } + else if ( ! createOptions.isEmpty() ) + { + QMessageBox::information( this, "", tr( "Cannot validate creation options" ), QMessageBox::Close ); + if ( tmpLayer ) + delete rasterLayer; + return QString(); + } - if ( gui ) + if ( gui ) + { + if ( message.isNull() ) { - if ( message.isNull() ) - { - if ( reportOK ) - QMessageBox::information( this, "", tr( "Valid" ), QMessageBox::Close ); - } - else - { - 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 ); - } + if ( reportOK ) + QMessageBox::information( this, "", tr( "Valid" ), QMessageBox::Close ); + } + else + { + QMessageBox::warning( this, "", tr( "Invalid%1creation option :\n\n%2\n\nClick on help button to get valid creation options for this format" ).arg( mPyramids ? " pyramids " : " " ).arg( message ), QMessageBox::Close ); } } - else - { - QMessageBox::information( this, "", tr( "Cannot validate" ), QMessageBox::Close ); - } + + if ( tmpLayer ) + delete rasterLayer; return message; } @@ -508,9 +565,9 @@ void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( int newIndex ) void QgsRasterFormatSaveOptionsWidget::updateControls() { - bool enabled = ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" ); - mOptionsValidateButton->setEnabled( enabled ); - mOptionsHelpButton->setEnabled( enabled ); + bool valid = mProvider == "gdal" && mFormat != ""; + mOptionsValidateButton->setEnabled( valid ); + mOptionsHelpButton->setEnabled( valid ); } // map options label left mouse click to optionsToggle() @@ -546,3 +603,10 @@ bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event return QObject::eventFilter( obj, event ); } +void QgsRasterFormatSaveOptionsWidget::showEvent( QShowEvent * event ) +{ + Q_UNUSED( event ); + mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 ); + QgsDebugMsg( "done" ); +} + diff --git a/src/gui/qgsrasterformatsaveoptionswidget.h b/src/gui/qgsrasterformatsaveoptionswidget.h index 0eacdc254af..f7343da020a 100644 --- a/src/gui/qgsrasterformatsaveoptionswidget.h +++ b/src/gui/qgsrasterformatsaveoptionswidget.h @@ -20,6 +20,8 @@ #include "ui_qgsrasterformatsaveoptionswidgetbase.h" +#include "qgsrasterdataprovider.h" + class QgsRasterLayer; /** \ingroup gui @@ -48,9 +50,12 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget, void setFormat( QString format ); void setProvider( QString provider ); - void setRasterLayer( QgsRasterLayer* rasterLayer ) { mRasterLayer = rasterLayer; } + void setRasterLayer( QgsRasterLayer* rasterLayer ) { mRasterLayer = rasterLayer; mRasterFileName = QString(); } + void setRasterFileName( const QString& file ) { mRasterLayer = 0; mRasterFileName = file; } QStringList options() const; void setType( QgsRasterFormatSaveOptionsWidget::Type type = Default ); + void setPyramidsFormat( QgsRasterDataProvider::RasterPyramidsFormat format ) + { mPyramids = true; mPyramidsFormat = format; } public slots: @@ -73,13 +78,22 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget, void swapOptionsUI( int newIndex = -1 ); void updateControls(); + protected: + virtual void showEvent( QShowEvent * event ); + + signals: + void optionsChanged(); + private: QString mFormat; QString mProvider; QgsRasterLayer* mRasterLayer; + QString mRasterFileName; QMap< QString, QString> mOptionsMap; static QMap< QString, QStringList > mBuiltinProfiles; + bool mPyramids; + QgsRasterDataProvider::RasterPyramidsFormat mPyramidsFormat; QString settingsKey( QString profile ) const; QString currentProfileKey() const; diff --git a/src/gui/qgsrasterlayersaveasdialog.cpp b/src/gui/qgsrasterlayersaveasdialog.cpp index 065f122250b..4048d7b0fd5 100644 --- a/src/gui/qgsrasterlayersaveasdialog.cpp +++ b/src/gui/qgsrasterlayersaveasdialog.cpp @@ -85,6 +85,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer* rasterLa mMaximumSizeYLineEdit->setText( QString::number( 2000 ) ); } + // setup creation option widget mCreateOptionsWidget->setProvider( mDataProvider->name() ); if ( mDataProvider->name() == "gdal" ) { @@ -97,7 +98,9 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer* rasterLa // Only do pyramids if dealing directly with GDAL. if ( mDataProvider->capabilities() & QgsRasterDataProvider::BuildPyramids ) { - mPyramidsOptionsWidget->createOptionsWidget()->setType( QgsRasterFormatSaveOptionsWidget::ProfileLineEdit ); + // setup pyramids option widget + // mPyramidsOptionsWidget->createOptionsWidget()->setType( QgsRasterFormatSaveOptionsWidget::ProfileLineEdit ); + mPyramidsOptionsWidget->createOptionsWidget()->setRasterLayer( mRasterLayer ); // TODO enable "use existing", has no effect for now, because using Create() in gdal provider // if ( ! mDataProvider->hasPyramids() ) @@ -732,12 +735,13 @@ void QgsRasterLayerSaveAsDialog::on_mPyramidsGroupBox_toggled( bool toggled ) void QgsRasterLayerSaveAsDialog::populatePyramidsLevels() { - // if selection != "Build pyramids", get pyramids from actual layer QString text; if ( mPyramidsGroupBox->isChecked() ) { QList myPyramidList; + // if use existing, get pyramids from actual layer + // but that's not available yet if ( mPyramidsUseExistingCheckBox->isChecked() ) { myPyramidList = mDataProvider->buildPyramidList(); @@ -752,7 +756,7 @@ void QgsRasterLayerSaveAsDialog::populatePyramidsLevels() myRasterPyramidIterator != myPyramidList.end(); ++myRasterPyramidIterator ) { - if ( myRasterPyramidIterator->exists ) + if ( ! mPyramidsUseExistingCheckBox->isChecked() || myRasterPyramidIterator->exists ) { text += QString::number( myRasterPyramidIterator->xDim ) + QString( "x" ) + QString::number( myRasterPyramidIterator->yDim ) + " "; @@ -809,7 +813,7 @@ QList QgsRasterLayerSaveAsDialog::noData() const return noDataList; } -QList QgsRasterLayerSaveAsDialog::overviewList() const +QList QgsRasterLayerSaveAsDialog::pyramidsList() const { return mPyramidsGroupBox->isChecked() ? mPyramidsOptionsWidget->overviewList() : QList(); } @@ -832,6 +836,12 @@ bool QgsRasterLayerSaveAsDialog::validate() const if ( !message.isNull() ) return false; } + if ( mPyramidsGroupBox->isChecked() ) + { + QString message = mPyramidsOptionsWidget->createOptionsWidget()->validateOptions( true, false ); + if ( !message.isNull() ) + return false; + } return true; } diff --git a/src/gui/qgsrasterlayersaveasdialog.h b/src/gui/qgsrasterlayersaveasdialog.h index fcd3cc6f649..a1f6233fa60 100644 --- a/src/gui/qgsrasterlayersaveasdialog.h +++ b/src/gui/qgsrasterlayersaveasdialog.h @@ -72,11 +72,13 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast QgsRectangle outputRectangle() const; QList noData() const; - QList< int > overviewList() const; + QList< int > pyramidsList() const; QgsRasterDataProvider::RasterBuildPyramids buildPyramidsFlag() const; - QString pyramidsResampling() const { return mPyramidsOptionsWidget->resamplingMethod(); } + QString pyramidsResamplingMethod() const { return mPyramidsOptionsWidget->resamplingMethod(); } QgsRasterDataProvider::RasterPyramidsFormat pyramidsFormat() const { return mPyramidsOptionsWidget->pyramidsFormat(); } + QStringList pyramidsConfigOptions() const + { return mPyramidsOptionsWidget->configOptions(); } void hideFormat(); void hideOutput(); diff --git a/src/gui/qgsrasterpyramidsoptionswidget.cpp b/src/gui/qgsrasterpyramidsoptionswidget.cpp index daea2ccb9d5..48ee1b2ad4a 100644 --- a/src/gui/qgsrasterpyramidsoptionswidget.cpp +++ b/src/gui/qgsrasterpyramidsoptionswidget.cpp @@ -33,9 +33,9 @@ QgsRasterPyramidsOptionsWidget::QgsRasterPyramidsOptionsWidget( QWidget* parent, { setupUi( this ); - mPyramidsOptionsWidget->setProvider( provider ); - mPyramidsOptionsWidget->setFormat( "_pyramids" ); - // mPyramidsOptionsWidget->swapOptionsUI( 1 ); + mSaveOptionsWidget->setProvider( provider ); + mSaveOptionsWidget->setPyramidsFormat( QgsRasterDataProvider::PyramidsGTiff ); + mSaveOptionsWidget->setType( QgsRasterFormatSaveOptionsWidget::ProfileLineEdit ); updateUi(); } @@ -68,8 +68,9 @@ void QgsRasterPyramidsOptionsWidget::updateUi() mySettings.value( prefix + "resampling", "Average" ).toString() ) ); // validate string, only space-separated positive integers are allowed + lePyramidsLevels->setEnabled( cbxPyramidsLevelsCustom->isChecked() ); lePyramidsLevels->setValidator( new QRegExpValidator( QRegExp( "(\\d*)(\\s\\d*)*" ), lePyramidsLevels ) ); - connect( lePyramidsLevels, SIGNAL( editingFinished() ), + connect( lePyramidsLevels, SIGNAL( textEdited( const QString & ) ), this, SLOT( setOverviewList() ) ); // overview list @@ -99,14 +100,19 @@ void QgsRasterPyramidsOptionsWidget::updateUi() } setOverviewList(); - mPyramidsOptionsWidget->updateProfiles(); + mSaveOptionsWidget->updateProfiles(); + + connect( cbxPyramidsFormat, SIGNAL( currentIndexChanged( int ) ), + this, SIGNAL( someValueChanged() ) ); + connect( cboResamplingMethod, SIGNAL( currentIndexChanged( int ) ), + this, SIGNAL( someValueChanged() ) ); + connect( mSaveOptionsWidget, SIGNAL( optionsChanged() ), + this, SIGNAL( someValueChanged() ) ); } - - QString QgsRasterPyramidsOptionsWidget::resamplingMethod() const { - return cboResamplingMethod->currentText().trimmed(); + return QgsRasterDataProvider::pyramidResamplingArg( cboResamplingMethod->currentText().trimmed() ); } void QgsRasterPyramidsOptionsWidget::apply() @@ -135,7 +141,7 @@ void QgsRasterPyramidsOptionsWidget::apply() } mySettings.setValue( prefix + "overviewList", tmpStr.trimmed() ); - mPyramidsOptionsWidget->apply(); + mSaveOptionsWidget->apply(); } void QgsRasterPyramidsOptionsWidget::checkAllLevels( bool checked ) @@ -153,13 +159,19 @@ void QgsRasterPyramidsOptionsWidget::on_cbxPyramidsLevelsCustom_toggled( bool to setOverviewList(); } +void QgsRasterPyramidsOptionsWidget::on_cbxPyramidsFormat_currentIndexChanged( int index ) +{ + mSaveOptionsWidget->setEnabled( index != 2 ); + mSaveOptionsWidget->setPyramidsFormat(( QgsRasterDataProvider::RasterPyramidsFormat ) index ); +} + void QgsRasterPyramidsOptionsWidget::setOverviewList() { QgsDebugMsg( "Entered" ); mOverviewList.clear(); - // if custum levels is toggled, get selection from line edit + // if custom levels is toggled, get selection from line edit if ( cbxPyramidsLevelsCustom->isChecked() ) { // should we also validate that numbers are increasing? diff --git a/src/gui/qgsrasterpyramidsoptionswidget.h b/src/gui/qgsrasterpyramidsoptionswidget.h index eacf8303109..25e13f11593 100644 --- a/src/gui/qgsrasterpyramidsoptionswidget.h +++ b/src/gui/qgsrasterpyramidsoptionswidget.h @@ -36,12 +36,14 @@ class GUI_EXPORT QgsRasterPyramidsOptionsWidget: public QWidget, QgsRasterPyramidsOptionsWidget( QWidget* parent = 0, QString provider = "gdal" ); ~QgsRasterPyramidsOptionsWidget(); - QStringList createOptions() const { return mPyramidsOptionsWidget->options(); } - QgsRasterFormatSaveOptionsWidget* createOptionsWidget() { return mPyramidsOptionsWidget; } + QStringList configOptions() const { return mSaveOptionsWidget->options(); } + QgsRasterFormatSaveOptionsWidget* createOptionsWidget() { return mSaveOptionsWidget; } const QList overviewList() const { return mOverviewList; } QgsRasterDataProvider::RasterPyramidsFormat pyramidsFormat() const { return ( QgsRasterDataProvider::RasterPyramidsFormat ) cbxPyramidsFormat->currentIndex(); } QString resamplingMethod() const; + void setRasterLayer( QgsRasterLayer* rasterLayer ) { mSaveOptionsWidget->setRasterLayer( rasterLayer ); } + void setRasterFileName( const QString& file ) { mSaveOptionsWidget->setRasterFileName( file ); } public slots: @@ -51,17 +53,16 @@ class GUI_EXPORT QgsRasterPyramidsOptionsWidget: public QWidget, private slots: void on_cbxPyramidsLevelsCustom_toggled( bool toggled ); - void on_cbxPyramidsFormat_currentIndexChanged( int index ) - { mPyramidsOptionsWidget->setEnabled( index != 2 ); } + void on_cbxPyramidsFormat_currentIndexChanged( int index ); void setOverviewList(); + void updateUi(); signals: void overviewListChanged(); + void someValueChanged(); /* emitted when any other setting changes */ private: - void updateUi(); - QString mProvider; QList< int > mOverviewList; QMap< int, QCheckBox* > mOverviewCheckBoxes; diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index d5d6e965c85..0fc749d5b1c 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -1321,8 +1321,9 @@ QgsRasterHistogram QgsGdalProvider::histogram( int theBandNo, * @param theTryInternalFlag - Try to make the pyramids internal if supported (e.g. geotiff). If not supported it will revert to creating external .ovr file anyway. * @return null string on success, otherwise a string specifying error */ -QString QgsGdalProvider::buildPyramids( QList const & theRasterPyramidList, - QString const & theResamplingMethod, RasterPyramidsFormat theFormat ) +QString QgsGdalProvider::buildPyramids( const QList & theRasterPyramidList, + const QString & theResamplingMethod, RasterPyramidsFormat theFormat, + const QStringList & theConfigOptions ) { //TODO: Consider making theRasterPyramidList modifyable by this method to indicate if the pyramid exists after build attempt //without requiring the user to rebuild the pyramid list to get the updated infomation @@ -1337,14 +1338,6 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste // TODO add signal and connect from rasterlayer //emit drawingProgress( 0, 0 ); - //first test if the file is writable - //QFileInfo myQFile( mDataSource ); - QFileInfo myQFile( dataSourceUri() ); - - if ( !myQFile.isWritable() ) - { - return "ERROR_WRITE_ACCESS"; - } if ( mGdalDataset != mGdalBaseDataset ) { @@ -1355,6 +1348,16 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste // check if building internally if ( theFormat == PyramidsInternal ) { + + // test if the file is writable + //QFileInfo myQFile( mDataSource ); + QFileInfo myQFile( dataSourceUri() ); + + if ( !myQFile.isWritable() ) + { + return "ERROR_WRITE_ACCESS"; + } + // libtiff < 4.0 has a bug that prevents safe building of overviews on JPEG compressed files // we detect libtiff < 4.0 by checking that BIGTIFF is not in the creation options of the GTiff driver // see https://trac.osgeo.org/qgis/ticket/1357 @@ -1369,9 +1372,12 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste } } - //if needed close the gdal dataset and reopen it in read / write mode + // if needed close the gdal dataset and reopen it in read / write mode + // TODO this doesn't seem to work anymore... must fix it before 2.0!!! + // no errors are reported, but pyramids are not present in file. if ( GDALGetAccess( mGdalDataset ) == GA_ReadOnly ) { + QgsDebugMsg( "re-opening the dataset in read/write mode" ); GDALClose( mGdalDataset ); //mGdalBaseDataset = GDALOpen( QFile::encodeName( dataSourceUri() ).constData(), GA_Update ); @@ -1389,12 +1395,29 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste } // are we using Erdas Imagine external overviews? - char* myConfigUseRRD = strdup( CPLGetConfigOption( "USE_RRD", "NO" ) ); + QgsStringMap myConfigOptionsOld; + myConfigOptionsOld[ "USE_RRD" ] = CPLGetConfigOption( "USE_RRD", "NO" ); if ( theFormat == PyramidsErdas ) CPLSetConfigOption( "USE_RRD", "YES" ); else CPLSetConfigOption( "USE_RRD", "NO" ); + // add any driver-specific configuration options, save values to be restored later + if ( theFormat != PyramidsErdas && ! theConfigOptions.isEmpty() ) + { + foreach ( QString option, theConfigOptions ) + { + QStringList opt = option.split( "=" ); + QByteArray key = opt[0].toLocal8Bit(); + QByteArray value = opt[1].toLocal8Bit(); + // save previous value + myConfigOptionsOld[ opt[0] ] = QString( CPLGetConfigOption( key.data(), NULL ) ); + // set temp. value + CPLSetConfigOption( key.data(), value.data() ); + QgsDebugMsg( QString( "set option %1=%2" ).arg( key.data() ).arg( value.data() ) ); + } + } + // // Iterate through the Raster Layer Pyramid Vector, building any pyramid // marked as exists in each RasterPyramid struct. @@ -1420,36 +1443,20 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste myOverviewLevelsVector.append( myRasterPyramidIterator->level ); } } - /* From : http://remotesensing.org/gdal/classGDALDataset.html#a23 - * pszResampling : one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", "AVERAGE_MAGPHASE" or "NONE" - * controlling the downsampling method applied. + /* From : http://www.gdal.org/classGDALDataset.html#a2aa6f88b3bbc840a5696236af11dde15 + * pszResampling : one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", "AVERAGE_MAGPHASE" or "NONE" controlling the downsampling method applied. * nOverviews : number of overviews to build. * panOverviewList : the list of overview decimation factors to build. - * nBand : number of bands to build overviews for in panBandList. Build for all bands if this is 0. + * nListBands : number of bands to build overviews for in panBandList. Build for all bands if this is 0. * panBandList : list of band numbers. * pfnProgress : a function to call to report progress, or NULL. * pProgressData : application data to pass to the progress function. */ - const char* theMethod; - if ( theResamplingMethod == tr( "Gauss" ) ) - theMethod = "GAUSS"; - else if ( theResamplingMethod == tr( "Cubic" ) ) - theMethod = "CUBIC"; - else if ( theResamplingMethod == tr( "Average" ) ) - theMethod = "AVERAGE"; - else if ( theResamplingMethod == tr( "Mode" ) ) - theMethod = "MODE"; - //NOTE magphase is disabled in the gui since it tends - //to create corrupted images. The images can be repaired - //by running one of the other resampling strategies below. - //see ticket #284 - // else if ( theResamplingMethod == tr( "Average Magphase" ) ) - // theMethod = "AVERAGE_MAGPHASE"; - else if ( theResamplingMethod == tr( "None" ) ) - theMethod = "NONE"; - else // fall back to nearest neighbor - theMethod = "NEAREST"; + // resampling method is now passed directly, via QgsRasterDataProvider::pyramidResamplingArg() + // average_mp and average_magphase have been removed from the gui + QByteArray ba = theResamplingMethod.toLocal8Bit(); + const char *theMethod = ba.data(); //build the pyramid and show progress to console QgsDebugMsg( QString( "Building overviews at %1 levels using resampling method %2" @@ -1467,7 +1474,7 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste if ( myError == CE_Failure || CPLGetLastErrorNo() == CPLE_NotSupported ) { - QgsDebugMsg( "Building pyramids failed" ); + QgsDebugMsg( QString( "Building pyramids failed using resampling method [%1]" ).arg( theMethod ) ); //something bad happenend //QString myString = QString (CPLGetLastError()); GDALClose( mGdalBaseDataset ); @@ -1476,9 +1483,17 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste mGdalDataset = mGdalBaseDataset; //emit drawingProgress( 0, 0 ); - // restore former USE_RRD config (Erdas) - CPLSetConfigOption( "USE_RRD", myConfigUseRRD ); - free( myConfigUseRRD ); + + // restore former configOptions + for ( QgsStringMap::const_iterator it = myConfigOptionsOld.begin(); + it != myConfigOptionsOld.end(); ++it ) + { + QByteArray key = it.key().toLocal8Bit(); + QByteArray value = it.value().toLocal8Bit(); + CPLSetConfigOption( key.data(), value.data() ); + } + + // TODO print exact error message return "FAILED_NOT_SUPPORTED"; } else @@ -1493,9 +1508,14 @@ QString QgsGdalProvider::buildPyramids( QList const & theRaste QgsLogger::warning( "Pyramid overview building failed!" ); } - // restore former USE_RRD config (Erdas) - CPLSetConfigOption( "USE_RRD", myConfigUseRRD ); - free( myConfigUseRRD ); + // restore former configOptions + for ( QgsStringMap::const_iterator it = myConfigOptionsOld.begin(); + it != myConfigOptionsOld.end(); ++it ) + { + QByteArray key = it.key().toLocal8Bit(); + QByteArray value = it.value().toLocal8Bit(); + CPLSetConfigOption( key.data(), value.data() ); + } QgsDebugMsg( "Pyramid overviews built" ); @@ -2521,13 +2541,21 @@ QGISEXTERN QString helpCreationOptionsFormat( QString format ) GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() ); if ( myGdalDriver ) { - // need to serialize xml to get newlines - // should we make the basic xml prettier? + // first report details and help page + char ** GDALmetadata = GDALGetMetadata( myGdalDriver, NULL ); + message += "Format Details:\n"; + message += QString( " Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) ); + message += QString( " Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) ); + message += QString( " / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) ); + message += QString( " Help page: http://www.gdal.org/%1\n\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_HELPTOPIC ) ); + + // next get creation options + // need to serialize xml to get newlines, should we make the basic xml prettier? CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver, GDAL_DMD_CREATIONOPTIONLIST, "" ) ); char *pszFormattedXML = CPLSerializeXMLTree( psCOL ); if ( pszFormattedXML ) - message = QString( pszFormattedXML ); + message += QString( pszFormattedXML ); if ( psCOL ) CPLDestroyXMLNode( psCOL ); if ( pszFormattedXML ) @@ -2608,3 +2636,38 @@ QString QgsGdalProvider::validateCreationOptions( const QStringList& createOptio return message; } + +QString QgsGdalProvider::validatePyramidsCreationOptions( RasterPyramidsFormat pyramidsFormat, + const QStringList & theConfigOptions, const QString & fileFormat ) +{ + // Erdas Imagine format does not support config options + if ( pyramidsFormat == PyramidsErdas ) + { + if ( ! theConfigOptions.isEmpty() ) + return "Erdas Imagine format does not support config options"; + else + return QString(); + } + // Internal pyramids format only supported for gtiff/georaster/hfa/jp2kak/mrsid/nitf files + else if ( pyramidsFormat == PyramidsInternal ) + { + QStringList supportedFormats; + supportedFormats << "gtiff" << "georaster" << "hfa" << "jp2kak" << "mrsid" << "nitf"; + if ( ! supportedFormats.contains( fileFormat.toLower() ) ) + return QString( "Internal pyramids format only supported for gtiff/georaster/hfa/jp2kak/mrsid/nitf files (using %1)" ).arg( fileFormat ); + // TODO - check arguments for georaster hfa jp2kak mrsid nitf + // for now, only test gtiff + else if ( fileFormat.toLower() != "gtiff" ) + return QString(); + } + + // for gtiff external or internal pyramids, validate gtiff-specific values + // PHOTOMETRIC_OVERVIEW=YCBCR requires a source raster with only 3 bands (RGB) + if ( theConfigOptions.contains( "PHOTOMETRIC_OVERVIEW=YCBCR" ) ) + { + if ( GDALGetRasterCount( mGdalDataset ) != 3 ) + return "PHOTOMETRIC_OVERVIEW=YCBCR requires a source raster with only 3 bands (RGB)"; + } + + return QString(); +} diff --git a/src/providers/gdal/qgsgdalprovider.h b/src/providers/gdal/qgsgdalprovider.h index 1efa02f2913..131a32bb5e1 100644 --- a/src/providers/gdal/qgsgdalprovider.h +++ b/src/providers/gdal/qgsgdalprovider.h @@ -221,9 +221,10 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase int theSampleSize = 0, bool theIncludeOutOfRange = false ); - QString buildPyramids( const QList &, - const QString & theResamplingMethod = "NEAREST", - RasterPyramidsFormat theFormat = PyramidsGTiff ); + QString buildPyramids( const QList & theRasterPyramidList, + const QString & theResamplingMethod = "NEAREST", + RasterPyramidsFormat theFormat = PyramidsGTiff, + const QStringList & theCreateOptions = QStringList() ); QList buildPyramidList( QList overviewList = QList() ); /** \brief Close data set and release related data */ @@ -257,6 +258,8 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase bool remove(); QString validateCreationOptions( const QStringList& createOptions, QString format ); + QString validatePyramidsCreationOptions( RasterPyramidsFormat pyramidsFormat, + const QStringList & theConfigOptions, const QString & fileFormat ); signals: void statusChanged( QString ); @@ -314,6 +317,7 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase /** \brief sublayers list saved for subsequent access */ QStringList mSubLayers; + }; #endif diff --git a/src/ui/qgsrasterpyramidsoptionswidgetbase.ui b/src/ui/qgsrasterpyramidsoptionswidgetbase.ui index 6806068e9b9..bdb38429908 100644 --- a/src/ui/qgsrasterpyramidsoptionswidgetbase.ui +++ b/src/ui/qgsrasterpyramidsoptionswidgetbase.ui @@ -7,7 +7,7 @@ 0 0 366 - 171 + 156 @@ -17,14 +17,17 @@ 1 - - - - Custom levels + + + + Insert positive integer values separated by spaces - + + + + @@ -34,7 +37,7 @@ - External + External (GTiff .ovr) @@ -44,74 +47,26 @@ - External (Erdas Imagine) + External (Erdas Imagine .aux) - - - - Insert positive integer values separated by spaces - - - - - - - - Average - - - - - Nearest Neighbour - - - - - - - - - - - Overview format - - - - - - - Create Options - - - - + Levels - - - - Qt::Horizontal - - - - - - - Resampling method - - - - + + + Create Options + + - + Qt::Horizontal @@ -127,6 +82,51 @@ + + + + Resampling method + + + + + + + + Average + + + + + Nearest Neighbour + + + + + + + + Custom levels + + + + + + + Qt::Horizontal + + + + + + + + + + Overview format + + +