From 212145d902f0fab1de6e84122e765856b46a1394 Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Wed, 19 Sep 2012 16:22:53 +0200 Subject: [PATCH 1/2] nodata per band, src nodata optional --- python/core/qgsrasterinterface.sip | 2 +- python/core/qgsrasterlayer.sip | 8 +- src/app/qgsrasterlayerproperties.cpp | 25 +++-- src/core/qgsrasterdataprovider.cpp | 36 +++++-- src/core/qgsrasterdataprovider.h | 51 ++++++---- src/core/raster/qgsrasterchecker.cpp | 3 +- src/core/raster/qgsrasterfilewriter.cpp | 14 +-- src/core/raster/qgsrasterinterface.cpp | 2 + src/core/raster/qgsrasterinterface.h | 10 +- src/core/raster/qgsrasterlayer.cpp | 33 ++++--- src/core/raster/qgsrasterlayer.h | 14 +-- src/core/raster/qgsrasterrenderer.cpp | 3 +- src/core/raster/qgsrasterrenderer.h | 44 --------- src/core/raster/qgsrastertransparency.cpp | 1 + src/providers/gdal/qgsgdalprovider.cpp | 88 +++++++++-------- src/providers/gdal/qgsgdalprovider.h | 4 +- src/providers/grass/qgis.d.rast.c | 21 +++- .../grass/qgsgrassrasterprovider.cpp | 25 +++-- src/providers/grass/qgsgrassrasterprovider.h | 4 +- src/providers/wcs/qgswcsprovider.cpp | 97 +++++++++---------- src/providers/wcs/qgswcsprovider.h | 4 +- src/ui/qgsrasterlayerpropertiesbase.ui | 7 +- tests/src/core/testqgsrasterfilewriter.cpp | 2 +- 23 files changed, 264 insertions(+), 234 deletions(-) diff --git a/python/core/qgsrasterinterface.sip b/python/core/qgsrasterinterface.sip index 2fa5f93319d8..329bacbdd2e3 100644 --- a/python/core/qgsrasterinterface.sip +++ b/python/core/qgsrasterinterface.sip @@ -82,7 +82,7 @@ class QgsRasterInterface virtual int bandCount() const; - virtual double noDataValue() const; + virtual double noDataValue( int bandNo ) const; virtual void * block( int bandNo, const QgsRectangle & extent, int width, int height ); diff --git a/python/core/qgsrasterlayer.sip b/python/core/qgsrasterlayer.sip index a68247bcf494..aee92fb743c8 100644 --- a/python/core/qgsrasterlayer.sip +++ b/python/core/qgsrasterlayer.sip @@ -172,7 +172,7 @@ public: int height(); /** \brief Is the NoDataValue Valid */ - bool isNoDataValueValid() const; + //bool isNoDataValueValid() const; /** \brief Accessor for mGrayMinimumMaximumEstimated */ //bool isGrayMinimumMaximumEstimated() const; //removed with raster redesign @@ -181,7 +181,7 @@ public: //bool isRGBMinimumMaximumEstimated() const; //removed with raster redesign /** \brief Accessor that returns the NO_DATA entry for this raster */ - double noDataValue( bool* isValid = 0 ); + //double noDataValue( bool* isValid = 0 ); /** \brief Returns a pointer to the transparency object */ //QgsRasterTransparency* rasterTransparency(); //removed with raster redesign @@ -385,7 +385,7 @@ public: //bool readColorTable( int theBandNumber, QList* theList ); /** \brief Simple reset function that set the noDataValue back to the value stored in the first raster band */ - void resetNoDataValue(); + //void resetNoDataValue(); /** \brief Mutator for blue band name mapping */ void setBlueBandName( const QString & theBandName ); @@ -431,7 +431,7 @@ public: void setMinimumValue( QString theBand, double theValue, bool theGenerateLookupTableFlag = true ); /** \brief Mutator that allows the NO_DATA entry for this raster to be overridden */ - void setNoDataValue( double theNoData ); + //void setNoDataValue( double theNoData ); /** \brief Set the raster shader function to a user defined function \note ownership of the shader function is transfered to raster shader */ diff --git a/src/app/qgsrasterlayerproperties.cpp b/src/app/qgsrasterlayerproperties.cpp index 3abf93251bab..ebc098fab77f 100644 --- a/src/app/qgsrasterlayerproperties.cpp +++ b/src/app/qgsrasterlayerproperties.cpp @@ -546,13 +546,20 @@ void QgsRasterLayerProperties::sync() // TODO: no data ranges if ( mRasterLayer->dataProvider()->srcHasNoDataValue( 1 ) ) { - lblSrcNoDataValue->setText( QgsRasterInterface::printValue( mRasterLayer->dataProvider()->noDataValue( 1 ) ) ); + lblSrcNoDataValue->setText( QgsRasterInterface::printValue( mRasterLayer->dataProvider()->srcNoDataValue( 1 ) ) ); } else { lblSrcNoDataValue->setText( tr( "not defined" ) ); } + mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSrcNoDataValue( 1 ) ); + + bool enableSrcNoData = mRasterLayer->dataProvider()->srcHasNoDataValue( 1 ) && !qIsNaN( mRasterLayer->dataProvider()->srcNoDataValue( 1 ) ); + + mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData ); + lblSrcNoDataValue->setEnabled( enableSrcNoData ); + QList noDataRangeList = mRasterLayer->dataProvider()->userNoDataValue( 1 ); QgsDebugMsg( QString( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) ); if ( noDataRangeList.size() > 0 ) @@ -600,13 +607,14 @@ void QgsRasterLayerProperties::sync() } else { - if ( mRasterLayer->isNoDataValueValid() ) + // TODO: all bands + if ( mRasterLayer->dataProvider()->srcHasNoDataValue( 1 ) ) { - lblNoData->setText( tr( "No-Data Value: %1" ).arg( mRasterLayer->noDataValue() ) ); + lblNoData->setText( tr( "No-Data Value: %1" ).arg( mRasterLayer->dataProvider()->noDataValue( 1 ) ) ); } else { - lblNoData->setText( tr( "No-Data Value: Not Set" ) ); + lblNoData->setText( tr( "No-Data Value: " ) + tr( "n/a" ) ); } } @@ -678,6 +686,7 @@ void QgsRasterLayerProperties::apply() for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ ) { mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList ); + mRasterLayer->dataProvider()->setUseSrcNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() ); } //set renderer from widget @@ -992,32 +1001,30 @@ void QgsRasterLayerProperties::on_pbnDefaultValues_clicked() setupTransparencyTable( nBands ); +// I don't think that noDataValue should be added to transparency list +#if 0 if ( nBands == 3 ) { if ( mRasterLayer->isNoDataValueValid() ) { - // I don't think that noDataValue should be added to transparency list -#if 0 tableTransparency->insertRow( tableTransparency->rowCount() ); setTransparencyCell( 0, 0, mRasterLayer->noDataValue() ); setTransparencyCell( 0, 1, mRasterLayer->noDataValue() ); setTransparencyCell( 0, 2, mRasterLayer->noDataValue() ); setTransparencyCell( 0, 1, 100 ); -#endif } } else //1 band { if ( mRasterLayer->isNoDataValueValid() ) { -#if 0 tableTransparency->insertRow( tableTransparency->rowCount() ); setTransparencyCell( 0, 0, mRasterLayer->noDataValue() ); setTransparencyCell( 0, 1, mRasterLayer->noDataValue() ); setTransparencyCell( 0, 1, 100 ); -#endif } } +#endif tableTransparency->resizeColumnsToContents(); // works only with values tableTransparency->resizeRowsToContents(); diff --git a/src/core/qgsrasterdataprovider.cpp b/src/core/qgsrasterdataprovider.cpp index 09a66c3b98a8..53e7d88f6000 100644 --- a/src/core/qgsrasterdataprovider.cpp +++ b/src/core/qgsrasterdataprovider.cpp @@ -25,6 +25,27 @@ #include +void QgsRasterDataProvider::setUseSrcNoDataValue( int bandNo, bool use ) +{ + if ( mUseSrcNoDataValue.size() < bandNo ) + { + for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ ) + { + mUseSrcNoDataValue.append( false ); + } + } + mUseSrcNoDataValue[bandNo-1] = use; +} + +double QgsRasterDataProvider::noDataValue( int bandNo ) const +{ + if ( mSrcHasNoDataValue.value( bandNo - 1 ) && mUseSrcNoDataValue.value( bandNo - 1 ) ) + { + return mSrcNoDataValue.value( bandNo -1 ); + } + return mInternalNoDataValue.value( bandNo -1 ); +} + void QgsRasterDataProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, @@ -289,7 +310,7 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo ) QByteArray ba; ba.resize(( int )size ); char * data = ba.data(); - double noval = mNoDataValue[theBandNo-1]; + double noval = noDataValue( theBandNo - 1 ); unsigned char uc; unsigned short us; short s; @@ -616,7 +637,6 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo, int myWidth = myRasterBandStats.width; int myHeight = myRasterBandStats.height; - double myNoDataValue = noDataValue(); int myDataType = dataType( theBandNo ); int myXBlockSize = xBlockSize(); @@ -669,7 +689,8 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo, double myValue = readValue( myData, myDataType, myX + ( myY * myBlockWidth ) ); //QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (myX).arg(myY).arg( myValue ) ); - if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) ) + // TODO: user nodata + if ( isNoDataValue( theBandNo, myValue ) ) { continue; // NULL } @@ -718,7 +739,6 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo, myRasterBandStats.stdDev = sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) ); QgsDebugMsg( "************ STATS **************" ); - QgsDebugMsg( QString( "VALID NODATA %1" ).arg( mValidNoDataValue ) ); QgsDebugMsg( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ) ); QgsDebugMsg( QString( "MAX %1" ).arg( myRasterBandStats.maximumValue ) ); QgsDebugMsg( QString( "RANGE %1" ).arg( myRasterBandStats.range ) ); @@ -893,7 +913,6 @@ QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo, QgsRectangle myExtent = myHistogram.extent; myHistogram.histogramVector.resize( myBinCount ); - double myNoDataValue = noDataValue(); int myDataType = dataType( theBandNo ); int myXBlockSize = xBlockSize(); @@ -953,7 +972,8 @@ QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo, double myValue = readValue( myData, myDataType, myX + ( myY * myBlockWidth ) ); //QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (myX).arg(myY).arg( myValue ) ); - if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) ) + // TODO: user defined nodata values + if ( isNoDataValue( theBandNo, myValue ) ) { continue; // NULL } @@ -1034,7 +1054,7 @@ void QgsRasterDataProvider::cumulativeCut( int theBandNo, double QgsRasterDataProvider::readValue( void *data, int type, int index ) { if ( !data ) - return mValidNoDataValue ? noDataValue() : 0.0; + return std::numeric_limits::quiet_NaN(); switch ( type ) { @@ -1063,7 +1083,7 @@ double QgsRasterDataProvider::readValue( void *data, int type, int index ) QgsLogger::warning( "GDAL data type is not supported" ); } - return mValidNoDataValue ? noDataValue() : 0.0; + return std::numeric_limits::quiet_NaN(); } void QgsRasterDataProvider::setUserNoDataValue( int bandNo, QList noData ) diff --git a/src/core/qgsrasterdataprovider.h b/src/core/qgsrasterdataprovider.h index 463527a8f699..668a64ebc27c 100644 --- a/src/core/qgsrasterdataprovider.h +++ b/src/core/qgsrasterdataprovider.h @@ -295,17 +295,24 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast virtual double readValue( void *data, int type, int index ); /* Return true if source band has no data value */ - virtual bool srcHasNoDataValue( int bandNo ) const { Q_UNUSED( bandNo ); return false; } + virtual bool srcHasNoDataValue( int bandNo ) const { return mSrcHasNoDataValue.value( bandNo -1 ); } + + /** \brief Get source nodata value usage */ + virtual bool useSrcNoDataValue( int bandNo ) const { return mUseSrcNoDataValue.value( bandNo -1 ); } + + /** \brief Set source nodata value usage */ + virtual void setUseSrcNoDataValue( int bandNo, bool use ); /** value representing null data */ - virtual double noDataValue() const { return 0; } + //virtual double noDataValue() const { return 0; } - /** Value representing original source no data. This value my differ from - * if it was overwritten by user */ - virtual double noDataValue( int bandNo ) const { return mNoDataValue.value( bandNo -1 ); } + /** Value representing currentno data. + * WARNING: this value returned by this method is not constant. It may change + * for example if user disable use of source no data value. */ + virtual double noDataValue( int bandNo ) const; /** Value representing no data value. */ - //virtual double srcNoDataValue( int bandNo ) const { return mSrcNoDataValue.value(bandNo-1); } + virtual double srcNoDataValue( int bandNo ) const { return mSrcNoDataValue.value( bandNo -1 ); } virtual void setUserNoDataValue( int bandNo, QList noData ); @@ -523,7 +530,7 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast void setDpi( int dpi ) {mDpi = dpi;} /** \brief Is the NoDataValue Valid */ - bool isNoDataValueValid() const { return mValidNoDataValue; } + //bool isNoDataValueValid() const { return mValidNoDataValue; } static QStringList cStringList2Q_( char ** stringList ); @@ -603,19 +610,27 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast int mDpi; /** \brief Cell value representing original source no data. e.g. -9999, indexed from 0 */ - //QList mSrcNoDataValue; - - /** \brief Cell value representing no data. e.g. -9999, indexed from 0. - * Currently the values is the same as in mSrcNoDataValue, but a possibility - * to overwrite/disable original no data value could be added (?), in that - * case this value would be different. But original source no data value - * cannot be disabled without add additional user nodata (there would not be - * any guaranteed free nodata value available (to represent nodata in - * reprojection for example) */ - QList mNoDataValue; + QList mSrcNoDataValue; + + /** \brief Source nodata value exist */ + QList mSrcHasNoDataValue; + + /** \brief Use source nodata value. User can disable usage of source nodata + * value as nodata. It may happen that a value is wrongly given by GDAL + * as nodata (e.g. 0) and it has to be treated as regular value. */ + QList mUseSrcNoDataValue; + + /** \brief Internal value representing nodata. Indexed from 0. + * This values is used to represent nodata if no source nodata is available + * or if the source nodata use was disabled. + * It would be also possible to use wider type only if nodata is really necessary + * in following interfaces, but that could make difficult to subclass + * QgsRasterInterface. + */ + QList mInternalNoDataValue; /** \brief Flag indicating if the nodatavalue is valid*/ - bool mValidNoDataValue; + //bool mValidNoDataValue; /** \brief List of lists of user defined additional no data values * for each band, indexed from 0 */ diff --git a/src/core/raster/qgsrasterchecker.cpp b/src/core/raster/qgsrasterchecker.cpp index 5d130f87912b..1a23266ebace 100644 --- a/src/core/raster/qgsrasterchecker.cpp +++ b/src/core/raster/qgsrasterchecker.cpp @@ -75,7 +75,6 @@ bool QgsRasterChecker::runTest( QString theVerifiedKey, QString theVerifiedUri, if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false; - compare( "No data (NULL) value", verifiedProvider->noDataValue(), expectedProvider->noDataValue(), mReport, ok ); mReport += "\n"; @@ -94,6 +93,8 @@ bool QgsRasterChecker::runTest( QString theVerifiedKey, QString theVerifiedUri, compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk ); compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk ) ; + compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk ); + bool statsOk = true; QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band ); QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band ); diff --git a/src/core/raster/qgsrasterfilewriter.cpp b/src/core/raster/qgsrasterfilewriter.cpp index 039e28a96897..df3efc3882aa 100644 --- a/src/core/raster/qgsrasterfilewriter.cpp +++ b/src/core/raster/qgsrasterfilewriter.cpp @@ -167,14 +167,15 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo ); bool destHasNoDataValue = false; double destNoDataValue; - //QgsRasterInterface::DataType destDataType = srcProvider->srcDataType( bandNo ); - QgsRasterInterface::DataType destDataType = srcProvider->dataType( bandNo ); + QgsRasterInterface::DataType destDataType = srcProvider->srcDataType( bandNo ); + //QgsRasterInterface::DataType destDataType = srcProvider->dataType( bandNo ); + // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue + QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) ); if ( srcHasNoDataValue ) { + // If source has no data value, it is used by provider - // TODO: this is not realy source no data, we would need srcNoDataValue() but it - // can be safely used I think - destNoDataValue = srcProvider->noDataValue(); + destNoDataValue = srcProvider->srcNoDataValue( bandNo ); destHasNoDataValue = true; } else if ( nuller && nuller->noData().size() > 0 ) @@ -255,6 +256,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs destDataTypeList.replace( i, destDataType ); destNoDataValueList.replace( i, destNoDataValue ); } + destDataType = destDataTypeList.value( 0 ); // Try again destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType ); @@ -367,7 +369,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( else { // TODO: this conversion should go to QgsRasterDataProvider::write with additional input data type param - void *destData = QgsRasterInterface::convert( dataList[i-1], srcProvider->srcDataType( i ), destDataType, iterCols * iterRows ); + void *destData = QgsRasterInterface::convert( dataList[i-1], srcProvider->dataType( i ), destDataType, iterCols * iterRows ); destDataList.push_back( destData ); CPLFree( dataList[i-1] ); } diff --git a/src/core/raster/qgsrasterinterface.cpp b/src/core/raster/qgsrasterinterface.cpp index 67e3a649e7e4..a210c5fcef8f 100644 --- a/src/core/raster/qgsrasterinterface.cpp +++ b/src/core/raster/qgsrasterinterface.cpp @@ -259,6 +259,8 @@ void * QgsRasterInterface::convert( void *srcData, QgsRasterInterface::DataType { double value = readValue( srcData, srcDataType, i ); writeValue( destData, destDataType, i, value ); + //double newValue = readValue( destData, destDataType, i ); + //QgsDebugMsg( QString("convert type %1 to %2: %3 -> %4").arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) ); } return destData; } diff --git a/src/core/raster/qgsrasterinterface.h b/src/core/raster/qgsrasterinterface.h index f00e5cbabd88..c9cee32e74de 100644 --- a/src/core/raster/qgsrasterinterface.h +++ b/src/core/raster/qgsrasterinterface.h @@ -142,14 +142,14 @@ class CORE_EXPORT QgsRasterInterface /** Retruns value representing 'no data' (NULL) */ // TODO: Q_DECL_DEPRECATED - virtual double noDataValue() const { return 0; } + //virtual double noDataValue() const { return 0; } /** Return no data value for specific band. Each band/provider must have * no data value, if there is no one set in original data, provider decides one * possibly using wider data type. * @param bandNo band number * @return No data value */ - virtual double noDataValue( int bandNo ) const { Q_UNUSED( bandNo ); return noDataValue(); } + virtual double noDataValue( int bandNo ) const { Q_UNUSED( bandNo ); return std::numeric_limits::quiet_NaN(); } /** Test if value is nodata for specific band * @param bandNo band number @@ -216,6 +216,10 @@ class CORE_EXPORT QgsRasterInterface * returned. */ double time( bool cumulative = false ); + inline static double readValue( void *data, QgsRasterInterface::DataType type, int index ); + + inline static void writeValue( void *data, QgsRasterInterface::DataType type, int index, double value ); + /** \brief Print double value with all necessary significant digits. * It is ensured that conversion back to double gives the same number. * @param value the value to be printed @@ -238,8 +242,6 @@ class CORE_EXPORT QgsRasterInterface // On/off state, if off, it does not do anything, replicates input bool mOn; - inline static double readValue( void *data, QgsRasterInterface::DataType type, int index ); - inline static void writeValue( void *data, QgsRasterInterface::DataType type, int index, double value ); /** \brief Test if value is within the list of ranges * @param value value diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index 2f9731f2c9b4..2ff31638fb1a 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -500,7 +500,7 @@ void QgsRasterLayer::computeMinimumMaximumFromLastExtent( int theBand, double* t if ( !theMinMax ) return; - int myDataType = mDataProvider->dataType( theBand ); + QgsRasterInterface::DataType myDataType = mDataProvider->dataType( theBand ); void* myScanData = readData( theBand, &mLastViewPort ); /* Check for out of memory error */ @@ -521,8 +521,8 @@ void QgsRasterLayer::computeMinimumMaximumFromLastExtent( int theBand, double* t { for ( int myColumn = 0; myColumn < mLastViewPort.drawableAreaXDim; ++myColumn ) { - myValue = readValue( myScanData, myDataType, myRow * mLastViewPort.drawableAreaXDim + myColumn ); - if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) ) + myValue = QgsRasterInterface::readValue( myScanData, myDataType, myRow * mLastViewPort.drawableAreaXDim + myColumn ); + if ( mDataProvider->isNoDataValue( theBand, myValue ) ) { continue; } @@ -1234,9 +1234,10 @@ QString QgsRasterLayer::metadata() myMetadata += tr( "No Data Value" ); myMetadata += "

\n"; myMetadata += "

"; - if ( mValidNoDataValue ) + // TODO: all bands + if ( mDataProvider->srcHasNoDataValue( 1 ) ) { - myMetadata += QString::number( mNoDataValue ); + myMetadata += QString::number( mDataProvider->srcNoDataValue( 1 ) ); } else { @@ -1545,7 +1546,7 @@ double QgsRasterLayer::rasterUnitsPerPixel() return 1; } - +#if 0 void QgsRasterLayer::resetNoDataValue() { mNoDataValue = std::numeric_limits::max(); @@ -1573,7 +1574,7 @@ void QgsRasterLayer::resetNoDataValue() mValidNoDataValue = mDataProvider->isNoDataValueValid(); } } - +#endif void QgsRasterLayer::setBlueBandName( QString const & theBandName ) { @@ -1596,8 +1597,8 @@ void QgsRasterLayer::init() setDrawingStyle( QgsRasterLayer::UndefinedDrawingStyle ); mBandCount = 0; - mNoDataValue = -9999.0; - mValidNoDataValue = false; + //mNoDataValue = -9999.0; + //mValidNoDataValue = false; //Initialize the last view port structure, should really be a class mLastViewPort.drawableAreaXDim = 0; @@ -1719,7 +1720,7 @@ void QgsRasterLayer::setDataProvider( QString const & provider ) mDataSource = mDataProvider->dataSourceUri(); } - setNoDataValue( mDataProvider->noDataValue() ); + //setNoDataValue( mDataProvider->noDataValue() ); // get the extent QgsRectangle mbr = mDataProvider->extent(); @@ -1737,7 +1738,7 @@ void QgsRasterLayer::setDataProvider( QString const & provider ) // upper case the first letter of the layer name QgsDebugMsg( "mLayerName: " + name() ); - mValidNoDataValue = mDataProvider->isNoDataValueValid(); + //mValidNoDataValue = mDataProvider->isNoDataValueValid(); // set up the raster drawing style // Do not set any 'sensible' style here, the style is set later @@ -2228,6 +2229,7 @@ void QgsRasterLayer::setMinimumValue( QString, double, bool ) //legacy method } +#if 0 void QgsRasterLayer::setNoDataValue( double theNoDataValue ) { if ( theNoDataValue != mNoDataValue ) @@ -2246,6 +2248,7 @@ void QgsRasterLayer::setNoDataValue( double theNoDataValue ) #endif } } +#endif void QgsRasterLayer::setRasterShaderFunction( QgsRasterShaderFunction* ) { @@ -2596,7 +2599,7 @@ bool QgsRasterLayer::readXml( const QDomNode& layer_node ) // Load user no data value QDomElement noDataElement = layer_node.firstChildElement( "noData" ); - QDomNodeList noDataBandList = noDataElement.elementsByTagName( "noDataRangeList" ); + QDomNodeList noDataBandList = noDataElement.elementsByTagName( "noDataList" ); for ( int i = 0; i < noDataBandList.size(); ++i ) { @@ -2606,6 +2609,7 @@ bool QgsRasterLayer::readXml( const QDomNode& layer_node ) QgsDebugMsg( QString( "bandNo = %1" ).arg( bandNo ) ); if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) ) { + mDataProvider->setUseSrcNoDataValue( bandNo, bandElement.attribute( "useSrcNoData" ).toInt() ); QList myNoDataRangeList; QDomNodeList rangeList = bandElement.elementsByTagName( "noDataRange" ); @@ -2686,8 +2690,9 @@ bool QgsRasterLayer::writeXml( QDomNode & layer_node, { if ( mDataProvider->userNoDataValue( bandNo ).isEmpty() ) continue; - QDomElement noDataRangeList = document.createElement( "noDataRangeList" ); + QDomElement noDataRangeList = document.createElement( "noDataList" ); noDataRangeList.setAttribute( "bandNo", bandNo ); + noDataRangeList.setAttribute( "useSrcNoData", mDataProvider->useSrcNoDataValue( bandNo ) ); foreach ( QgsRasterInterface::Range range, mDataProvider->userNoDataValue( bandNo ) ) { @@ -2816,6 +2821,7 @@ bool QgsRasterLayer::readFile( QString const &theFilename ) /* * @param index index in memory block */ +#if 0 double QgsRasterLayer::readValue( void *data, int type, int index ) { if ( !data ) @@ -2851,6 +2857,7 @@ double QgsRasterLayer::readValue( void *data, int type, int index ) return mValidNoDataValue ? mNoDataValue : 0.0; } +#endif bool QgsRasterLayer::update() { diff --git a/src/core/raster/qgsrasterlayer.h b/src/core/raster/qgsrasterlayer.h index fb116a7d6980..083ad590974e 100644 --- a/src/core/raster/qgsrasterlayer.h +++ b/src/core/raster/qgsrasterlayer.h @@ -342,10 +342,10 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer int height() { return mHeight; } /** \brief Is the NoDataValue Valid */ - bool isNoDataValueValid() const { return mValidNoDataValue; } + //bool isNoDataValueValid() const { return mValidNoDataValue; } /** \brief Accessor that returns the NO_DATA entry for this raster */ - double noDataValue( bool* isValid = 0 ) { if ( isValid ) { *isValid = mValidNoDataValue;} return mNoDataValue; } + //double noDataValue( bool* isValid = 0 ) { if ( isValid ) { *isValid = mValidNoDataValue;} return mNoDataValue; } /** \brief Accessor for raster layer type (which is a read only property) */ LayerType rasterType() { return mRasterType; } @@ -553,7 +553,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer bool readColorTable( int theBandNumber, QList* theList ); /** \brief Simple reset function that set the noDataValue back to the value stored in the first raster band */ - void resetNoDataValue(); + //void resetNoDataValue(); /** \brief Mutator for blue band name mapping */ void setBlueBandName( const QString & theBandName ); @@ -626,7 +626,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer Q_DECL_DEPRECATED void setMinimumValue( QString theBand, double theValue, bool theGenerateLookupTableFlag = true ); /** \brief Mutator that allows the NO_DATA entry for this raster to be overridden */ - void setNoDataValue( double theNoData ); + //void setNoDataValue( double theNoData ); /** \brief Set the raster shader function to a user defined function \note ownership of the shader function is transfered to raster shader */ @@ -795,7 +795,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer /** \brief Read a raster value given position from memory block created by readData() */ //inline double readValue( void *data, GDALDataType type, int index ); - inline double readValue( void *data, int type, int index ); + //inline double readValue( void *data, int type, int index ); /** \brief Update the layer if it is outdated */ bool update(); @@ -869,7 +869,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer bool mModified; /** \brief Cell value representing no data. e.g. -9999 */ - double mNoDataValue; + //double mNoDataValue; /** [ data provider interface ] Data provider key */ QString mProviderKey; @@ -889,7 +889,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer bool mUserDefinedRGBMinimumMaximum; /** \brief Flag indicating if the nodatavalue is valid*/ - bool mValidNoDataValue; + //bool mValidNoDataValue; //QgsRasterRenderer* mRenderer; //QgsRasterResampleFilter *mResampleFilter; diff --git a/src/core/raster/qgsrasterrenderer.cpp b/src/core/raster/qgsrasterrenderer.cpp index be6271def33a..5c1796102c46 100644 --- a/src/core/raster/qgsrasterrenderer.cpp +++ b/src/core/raster/qgsrasterrenderer.cpp @@ -91,7 +91,8 @@ bool QgsRasterRenderer::usesTransparency( ) const { return true; } - return ( mAlphaBand > 0 || ( mRasterTransparency && !mRasterTransparency->isEmpty( mInput->noDataValue() ) ) || !doubleNear( mOpacity, 1.0 ) ); + // TODO: nodata per band + return ( mAlphaBand > 0 || ( mRasterTransparency && !mRasterTransparency->isEmpty( mInput->noDataValue( 1 ) ) ) || !doubleNear( mOpacity, 1.0 ) ); } void QgsRasterRenderer::setRasterTransparency( QgsRasterTransparency* t ) diff --git a/src/core/raster/qgsrasterrenderer.h b/src/core/raster/qgsrasterrenderer.h index e98ae113bfc0..6e4960dd1cb8 100644 --- a/src/core/raster/qgsrasterrenderer.h +++ b/src/core/raster/qgsrasterrenderer.h @@ -80,7 +80,6 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface virtual QList usesBands() const { return QList(); } protected: - inline double readValue( void *data, QgsRasterInterface::DataType type, int index ); /**Write upper class info into rasterrenderer element (called by writeXML method of subclasses)*/ void _writeXML( QDomDocument& doc, QDomElement& rasterRendererElem ) const; @@ -101,47 +100,4 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface double mMaxOversampling; }; -inline double QgsRasterRenderer::readValue( void *data, QgsRasterInterface::DataType type, int index ) -{ - if ( !mInput ) - { - return 0; - } - - if ( !data ) - { - return mInput->noDataValue(); - } - - switch ( type ) - { - case QgsRasterInterface::Byte: - return ( double )(( GByte * )data )[index]; - break; - case QgsRasterInterface::UInt16: - return ( double )(( GUInt16 * )data )[index]; - break; - case QgsRasterInterface::Int16: - return ( double )(( GInt16 * )data )[index]; - break; - case QgsRasterInterface::UInt32: - return ( double )(( GUInt32 * )data )[index]; - break; - case QgsRasterInterface::Int32: - return ( double )(( GInt32 * )data )[index]; - break; - case QgsRasterInterface::Float32: - return ( double )(( float * )data )[index]; - break; - case QgsRasterInterface::Float64: - return ( double )(( double * )data )[index]; - break; - default: - //QgsMessageLog::logMessage( tr( "GDAL data type %1 is not supported" ).arg( type ), tr( "Raster" ) ); - break; - } - - return mInput->noDataValue(); -} - #endif // QGSRASTERRENDERER_H diff --git a/src/core/raster/qgsrastertransparency.cpp b/src/core/raster/qgsrastertransparency.cpp index 1c0afad822b4..3cf6047d601c 100644 --- a/src/core/raster/qgsrastertransparency.cpp +++ b/src/core/raster/qgsrastertransparency.cpp @@ -177,6 +177,7 @@ int QgsRasterTransparency::alphaValue( double theRedValue, double theGreenValue, return theGlobalTransparency; } +// TODO: nodata per band bool QgsRasterTransparency::isEmpty( double nodataValue ) const { return ( diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index cc6894fc8b81..64f0a0752d5a 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -731,6 +731,7 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent, } #endif +#if 0 bool QgsGdalProvider::srcHasNoDataValue( int bandNo ) const { if ( mGdalDataset ) @@ -754,6 +755,7 @@ double QgsGdalProvider::noDataValue() const } return std::numeric_limits::max(); // should not happen or be used } +#endif void QgsGdalProvider::computeMinMax( int theBandNo ) { @@ -834,7 +836,7 @@ QMap QgsGdalProvider::identify( const QgsPoint & point ) for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ ) { void * data = VSIMalloc( dataTypeSize( i ) / 8 ); - writeValue( data, dataType( i ), 0, noDataValue() ); + writeValue( data, dataType( i ), 0, noDataValue( i ) ); results.insert( i, data ); } } @@ -2127,7 +2129,6 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo, int theStats, #ifdef QGISDEBUG QgsDebugMsg( "************ STATS **************" ); - QgsDebugMsg( QString( "VALID NODATA %1" ).arg( mValidNoDataValue ) ); QgsDebugMsg( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ) ); QgsDebugMsg( QString( "MAX %1" ).arg( myRasterBandStats.maximumValue ) ); QgsDebugMsg( QString( "RANGE %1" ).arg( myRasterBandStats.range ) ); @@ -2273,57 +2274,64 @@ void QgsGdalProvider::initBaseDataset() // // Determine the nodata value and data type // - mValidNoDataValue = true; + //mValidNoDataValue = true; for ( int i = 1; i <= GDALGetRasterCount( mGdalBaseDataset ); i++ ) { computeMinMax( i ); GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i ); GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand ); + int isValid = false; - double myNoDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( mGdalDataset, i ), &isValid ); + double myNoDataValue = GDALGetRasterNoDataValue( myGdalBand, &isValid ); if ( isValid ) { QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1" ).arg( myNoDataValue ) ) ; - mGdalDataType.append( myGdalDataType ); + mSrcNoDataValue.append( myNoDataValue ); + mSrcHasNoDataValue.append( true ); + mUseSrcNoDataValue.append( true ); } else { - // But we need a null value in case of reprojection and BTW also for - // aligned margines - - switch ( srcDataType( i ) ) - { - case QgsRasterDataProvider::Byte: - // Use longer data type to avoid conflict with real data - myNoDataValue = -32768.0; - mGdalDataType.append( GDT_Int16 ); - break; - case QgsRasterDataProvider::Int16: - myNoDataValue = -2147483648.0; - mGdalDataType.append( GDT_Int32 ); - break; - case QgsRasterDataProvider::UInt16: - myNoDataValue = -2147483648.0; - mGdalDataType.append( GDT_Int32 ); - break; - case QgsRasterDataProvider::Int32: - myNoDataValue = -2147483648.0; - mGdalDataType.append( myGdalDataType ); - break; - case QgsRasterDataProvider::UInt32: - myNoDataValue = 4294967295.0; - mGdalDataType.append( myGdalDataType ); - break; - default: - myNoDataValue = std::numeric_limits::max(); - // Would NaN work well? - //myNoDataValue = std::numeric_limits::quiet_NaN(); - mGdalDataType.append( myGdalDataType ); - } + mSrcNoDataValue.append( std::numeric_limits::quiet_NaN() ); + mSrcHasNoDataValue.append( false ); + mUseSrcNoDataValue.append( false ); + } + // It may happen that nodata value given by GDAL is wrong and it has to be + // disabled by user, in that case we need another value to be used for nodata + // (for reprojection for example) -> always internaly represent as wider type + // with mInternalNoDataValue in reserve. + int myInternalGdalDataType = myGdalDataType; + double myInternalNoDataValue = 123; + switch ( srcDataType( i ) ) + { + case QgsRasterDataProvider::Byte: + myInternalNoDataValue = -32768.0; + myInternalGdalDataType = GDT_Int16; + break; + case QgsRasterDataProvider::Int16: + myInternalNoDataValue = -2147483648.0; + myInternalGdalDataType = GDT_Int32; + break; + case QgsRasterDataProvider::UInt16: + myInternalNoDataValue = -2147483648.0; + myInternalGdalDataType = GDT_Int32; + break; + case QgsRasterDataProvider::Int32: + // We believe that such values is no used in real data + myInternalNoDataValue = -2147483648.0; + break; + case QgsRasterDataProvider::UInt32: + // We believe that such values is no used in real data + myInternalNoDataValue = 4294967295.0; + break; + default: // Float32, Float64 + //myNoDataValue = std::numeric_limits::max(); + // NaN should work well + myInternalNoDataValue = std::numeric_limits::quiet_NaN(); } - //mSrcNoDataValue.append( myNoDataValue ); - mNoDataValue.append( myNoDataValue ); - QgsDebugMsg( QString( "mNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) ); + mGdalDataType.append( myInternalGdalDataType ); + mInternalNoDataValue.append( myInternalNoDataValue ); + QgsDebugMsg( QString( "mInternalNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mInternalNoDataValue[i-1] ) ); } mValid = true; diff --git a/src/providers/gdal/qgsgdalprovider.h b/src/providers/gdal/qgsgdalprovider.h index 53415539245a..8c8a033a7e16 100644 --- a/src/providers/gdal/qgsgdalprovider.h +++ b/src/providers/gdal/qgsgdalprovider.h @@ -198,8 +198,8 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase //void * readBlock( int bandNo, QgsRectangle const & extent, int width, int height ); - bool srcHasNoDataValue( int bandNo ) const; - double noDataValue() const; + //bool srcHasNoDataValue( int bandNo ) const; + //double noDataValue() const; void computeMinMax( int bandNo ); double minimumValue( int bandNo ) const; double maximumValue( int bandNo ) const; diff --git a/src/providers/grass/qgis.d.rast.c b/src/providers/grass/qgis.d.rast.c index 8415f2290506..495b5201d07c 100644 --- a/src/providers/grass/qgis.d.rast.c +++ b/src/providers/grass/qgis.d.rast.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef WIN32 #include #include @@ -140,6 +141,15 @@ static int cell_draw( char *name, long one = 1; FILE *fo; int raster_size; +#ifdef NAN + double dnul = NAN; + float fnul = NAN; +#else + double dnul = strtod( "NAN", 0 ); + float fnul = strtof( "NAN", 0 ); + // another possibility would be nan()/nanf() - C99 + // and 0./0. if all fails +#endif big_endian = !( *(( char * )( &one ) ) ); @@ -213,18 +223,19 @@ static int cell_draw( char *name, // see comments in QgsGrassRasterProvider::noDataValue() if ( data_type == CELL_TYPE ) { - int nul = -2000000000; + //int nul = -2000000000; + int nul = -2147483648; fwrite( &nul , 4, 1, fo ); } else if ( data_type == DCELL_TYPE ) { - double nul = -1e+300; - fwrite( &nul , 8, 1, fo ); + //double nul = -1e+300; + fwrite( &dnul , 8, 1, fo ); } else if ( data_type == FCELL_TYPE ) { - double nul = -1e+30; - fwrite( &nul , 4, 1, fo ); + //double nul = -1e+30; + fwrite( &fnul , 4, 1, fo ); } } else diff --git a/src/providers/grass/qgsgrassrasterprovider.cpp b/src/providers/grass/qgsgrassrasterprovider.cpp index 6c7efa04770c..ecf6b45a942f 100644 --- a/src/providers/grass/qgsgrassrasterprovider.cpp +++ b/src/providers/grass/qgsgrassrasterprovider.cpp @@ -15,7 +15,7 @@ * (at your option) any later version. * * * ***************************************************************************/ - +#include #include "qgslogger.h" #include "qgsgrass.h" @@ -76,7 +76,7 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const & uri ) mTimestamp = dataTimestamp(); mRasterValue.start( mGisdbase, mLocation, mMapset, mMapName ); - mValidNoDataValue = true; + //mValidNoDataValue = true; mCrs = QgsGrass::crs( mGisdbase, mLocation ); QgsDebugMsg( "mCrs: " + mCrs.toWkt() ); @@ -92,10 +92,11 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const & uri ) // TODO: avoid showing these strange numbers in GUI // TODO: don't save no data values in project file, add a flag if value was defined by user + + double myInternalNoDataValue; if ( mGrassDataType == CELL_TYPE ) { - //limit: -2147483647; - mNoDataValue = -2000000000; + myInternalNoDataValue = -2147483648; } else if ( mGrassDataType == DCELL_TYPE ) { @@ -105,7 +106,8 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const & uri ) // No data value is shown in GUI, use some nice number. // Choose values with small representation error. // limit: 1.7976931348623157e+308 - mNoDataValue = -1e+300; + //myInternalNoDataValue = -1e+300; + myInternalNoDataValue = std::numeric_limits::quiet_NaN(); } else { @@ -115,9 +117,11 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const & uri ) } // limit: 3.40282347e+38 - mNoDataValue = -1e+30; + //myInternalNoDataValue = -1e+30; + myInternalNoDataValue = std::numeric_limits::quiet_NaN(); } - QgsDebugMsg( QString( "mNoDataValue = %1" ).arg( mNoDataValue ) ); + mInternalNoDataValue.append( myInternalNoDataValue ); + QgsDebugMsg( QString( "myInternalNoDataValue = %1" ).arg( myInternalNoDataValue ) ); // TODO: refresh mRows and mCols if raster was rewritten // We have to decide some reasonable block size, not to big to occupate too much @@ -285,11 +289,6 @@ void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const & viewEx memcpy( block, data.data(), size ); } -double QgsGrassRasterProvider::noDataValue() const -{ - return mNoDataValue; -} - double QgsGrassRasterProvider::minimumValue( int bandNo ) const { Q_UNUSED( bandNo ); @@ -428,7 +427,7 @@ QMap QgsGrassRasterProvider::identify( const QgsPoint & thePoint ) QString strValue = mRasterValue.value( thePoint.x(), thePoint.y() ); // attention, value tool does his own tricks with grass identify() so it stops to refresh values outside extent or null values e.g. - double value = noDataValue(); + double value = noDataValue( 1 ); if ( strValue != "out" && strValue != "null" ) { diff --git a/src/providers/grass/qgsgrassrasterprovider.h b/src/providers/grass/qgsgrassrasterprovider.h index b28d1ff806d0..a6a40141c516 100644 --- a/src/providers/grass/qgsgrassrasterprovider.h +++ b/src/providers/grass/qgsgrassrasterprovider.h @@ -209,7 +209,7 @@ class QgsGrassRasterProvider : public QgsRasterDataProvider void readBlock( int bandNo, int xBlock, int yBlock, void *data ); void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data ); - double noDataValue() const; + //double noDataValue() const; double minimumValue( int bandNo )const; double maximumValue( int bandNo )const; @@ -263,7 +263,7 @@ class QgsGrassRasterProvider : public QgsRasterDataProvider QgsGrassRasterValue mRasterValue; - double mNoDataValue; + //double mNoDataValue; }; #endif diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp index 2e430135efbe..5ef8108ce6ac 100644 --- a/src/providers/wcs/qgswcsprovider.cpp +++ b/src/providers/wcs/qgswcsprovider.cpp @@ -280,56 +280,62 @@ QgsWcsProvider::QgsWcsProvider( QString const &uri ) QgsDebugMsg( QString( "myGdalDataType[%1] = %2" ).arg( i - 1 ).arg( myGdalDataType ) ); mSrcGdalDataType.append( myGdalDataType ); - // TODO: This could be shared with GDAL provider - int isValid = false; // UMN Mapserver does not automaticaly set null value, METADATA wcs_rangeset_nullvalue must be used // http://lists.osgeo.org/pipermail/mapserver-users/2010-April/065328.html - // TODO: - + // TODO: This could be shared with GDAL provider + int isValid = false; double myNoDataValue = GDALGetRasterNoDataValue( gdalBand, &isValid ); if ( isValid ) { QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1" ).arg( myNoDataValue ) ) ; - mGdalDataType.append( myGdalDataType ); + mSrcNoDataValue.append( myNoDataValue ); + mSrcHasNoDataValue.append( true ); + mUseSrcNoDataValue.append( true ); } else { - // But we need a null value in case of reprojection and BTW also for - // aligned margines - switch ( dataTypeFromGdal( myGdalDataType ) ) - { - - case QgsRasterDataProvider::Byte: - // Use longer data type to avoid conflict with real data - myNoDataValue = -32768.0; - mGdalDataType.append( GDT_Int16 ); - break; - case QgsRasterDataProvider::Int16: - myNoDataValue = -2147483648.0; - mGdalDataType.append( GDT_Int32 ); - break; - case QgsRasterDataProvider::UInt16: - myNoDataValue = -2147483648.0; - mGdalDataType.append( GDT_Int32 ); - break; - case QgsRasterDataProvider::Int32: - myNoDataValue = -2147483648.0; - mGdalDataType.append( myGdalDataType ); - break; - case QgsRasterDataProvider::UInt32: - myNoDataValue = 4294967295.0; - mGdalDataType.append( myGdalDataType ); - break; - default: - myNoDataValue = std::numeric_limits::max(); - // Would NaN work well? - //myNoDataValue = std::numeric_limits::quiet_NaN(); - mGdalDataType.append( myGdalDataType ); - } + mSrcNoDataValue.append( std::numeric_limits::quiet_NaN() ); + mSrcHasNoDataValue.append( false ); + mUseSrcNoDataValue.append( false ); } - mNoDataValue.append( myNoDataValue ); + // It may happen that nodata value given by GDAL is wrong and it has to be + // disabled by user, in that case we need another value to be used for nodata + // (for reprojection for example) -> always internaly represent as wider type + // with mInternalNoDataValue in reserve. + int myInternalGdalDataType = myGdalDataType; + double myInternalNoDataValue; + switch ( srcDataType( i ) ) + { + case QgsRasterDataProvider::Byte: + myInternalNoDataValue = -32768.0; + myInternalGdalDataType = GDT_Int16; + break; + case QgsRasterDataProvider::Int16: + myInternalNoDataValue = -2147483648.0; + myInternalGdalDataType = GDT_Int32; + break; + case QgsRasterDataProvider::UInt16: + myInternalNoDataValue = -2147483648.0; + myInternalGdalDataType = GDT_Int32; + break; + case QgsRasterDataProvider::Int32: + // We believe that such values is no used in real data + myInternalNoDataValue = -2147483648.0; + break; + case QgsRasterDataProvider::UInt32: + // We believe that such values is no used in real data + myInternalNoDataValue = 4294967295.0; + break; + default: // Float32, Float64 + //myNoDataValue = std::numeric_limits::max(); + // NaN should work well + myInternalNoDataValue = std::numeric_limits::quiet_NaN(); + } + mGdalDataType.append( myInternalGdalDataType ); + mInternalNoDataValue.append( myInternalNoDataValue ); + QgsDebugMsg( QString( "mInternalNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mInternalNoDataValue[i-1] ) ); // TODO: what to do if null values from DescribeCoverage differ? if ( !mCoverageSummary.nullValues.contains( myNoDataValue ) ) @@ -337,11 +343,9 @@ QgsWcsProvider::QgsWcsProvider( QString const &uri ) QgsDebugMsg( QString( "noDataValue %1 is missing in nullValues from CoverageDescription" ).arg( myNoDataValue ) ); } - mValidNoDataValue = true; - QgsDebugMsg( QString( "mSrcGdalDataType[%1] = %2" ).arg( i - 1 ).arg( mSrcGdalDataType[i-1] ) ); QgsDebugMsg( QString( "mGdalDataType[%1] = %2" ).arg( i - 1 ).arg( mGdalDataType[i-1] ) ); - QgsDebugMsg( QString( "mNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) ); + QgsDebugMsg( QString( "mSrcNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mSrcNoDataValue[i-1] ) ); // Create and store color table // TODO: never tested because mapserver (6.0.3) does not support color tables @@ -1074,15 +1078,6 @@ int QgsWcsProvider::bandCount() const return mBandCount; } -double QgsWcsProvider::noDataValue() const -{ - if ( mNoDataValue.size() > 0 ) - { - return mNoDataValue[0]; - } - return std::numeric_limits::max(); // should not happen or be used -} - // this is only called once when statistics are calculated // TODO int QgsWcsProvider::xBlockSize() const @@ -1525,7 +1520,7 @@ QMap QgsWcsProvider::identify( const QgsPoint & thePoint ) for ( int i = 1; i <= bandCount(); i++ ) { void * data = VSIMalloc( dataTypeSize( i ) / 8 ); - writeValue( data, dataType( i ), 0, noDataValue() ); + writeValue( data, dataType( i ), 0, noDataValue( i ) ); results.insert( i, data ); } return results; diff --git a/src/providers/wcs/qgswcsprovider.h b/src/providers/wcs/qgswcsprovider.h index f583a2e01439..eaacc6aaa7aa 100644 --- a/src/providers/wcs/qgswcsprovider.h +++ b/src/providers/wcs/qgswcsprovider.h @@ -147,7 +147,7 @@ class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase QgsRasterInterface::DataType dataType( int bandNo ) const; QgsRasterInterface::DataType srcDataType( int bandNo ) const; int bandCount() const; - double noDataValue() const; + //double noDataValue() const; int xBlockSize() const; int yBlockSize() const; int xSize() const; @@ -303,7 +303,7 @@ class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase QListmSrcGdalDataType; /** \brief Cell value representing no data. e.g. -9999, indexed from 0 */ - QList mNoDataValue; + //QList mNoDataValue; /** Color tables indexed from 0 */ QList< QList > mColorTables; diff --git a/src/ui/qgsrasterlayerpropertiesbase.ui b/src/ui/qgsrasterlayerpropertiesbase.ui index 158ec0c2f113..1875c2df8df7 100644 --- a/src/ui/qgsrasterlayerpropertiesbase.ui +++ b/src/ui/qgsrasterlayerpropertiesbase.ui @@ -439,9 +439,12 @@ 1 - + + + Use original source no data value. + - Source no data value: + No data value: diff --git a/tests/src/core/testqgsrasterfilewriter.cpp b/tests/src/core/testqgsrasterfilewriter.cpp index a5133756816e..cb977b2bda07 100644 --- a/tests/src/core/testqgsrasterfilewriter.cpp +++ b/tests/src/core/testqgsrasterfilewriter.cpp @@ -166,7 +166,7 @@ bool TestQgsRasterFileWriter::writeTest( QString theRasterName ) // All OK, we can delete the file tmpFile.setAutoRemove( ok ); - return true; + return ok; } void TestQgsRasterFileWriter::log( QString msg ) From b1e00e950c799c273a3402dd2ae32d6d06f8da73 Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Wed, 19 Sep 2012 22:58:39 +0200 Subject: [PATCH 2/2] wcs test - improved report, servers list in json --- src/providers/wcs/qgswcscapabilities.cpp | 76 ++++--- src/providers/wcs/qgswcscapabilities.h | 15 +- tests/src/providers/CMakeLists.txt | 3 + .../src/providers/testqgswcspublicservers.cpp | 193 +++++++++++++----- tests/src/providers/testqgswcspublicservers.h | 6 +- tests/src/providers/wcs-servers.json | 59 ++++++ 6 files changed, 264 insertions(+), 88 deletions(-) create mode 100644 tests/src/providers/wcs-servers.json diff --git a/src/providers/wcs/qgswcscapabilities.cpp b/src/providers/wcs/qgswcscapabilities.cpp index 7a5b9a3823e6..e4bf0f130e82 100644 --- a/src/providers/wcs/qgswcscapabilities.cpp +++ b/src/providers/wcs/qgswcscapabilities.cpp @@ -187,6 +187,32 @@ void QgsWcsCapabilities::clear() mCapabilities = c; } +QString QgsWcsCapabilities::getCapabilitiesUrl( const QString version ) const +{ + QString url = prepareUri( mUri.param( "url" ) ) + "SERVICE=WCS&REQUEST=GetCapabilities"; + + if ( !version.isEmpty() ) + { + // 1.0.0 - VERSION + // 1.1.0 - AcceptVersions (not supported by UMN Mapserver 6.0.3 - defaults to latest 1.1 + if ( version.startsWith( "1.0" ) ) + { + url += "&VERSION=" + version; + } + else if ( version.startsWith( "1.1" ) ) + { + // Ignored by UMN Mapserver 6.0.3, see below + url += "&AcceptVersions=" + version; + } + } + return url; +} + +QString QgsWcsCapabilities::getCapabilitiesUrl( ) const +{ + return getCapabilitiesUrl( mVersion ); +} + bool QgsWcsCapabilities::retrieveServerCapabilities( ) { clear(); @@ -196,21 +222,10 @@ bool QgsWcsCapabilities::retrieveServerCapabilities( ) if ( !preferredVersion.isEmpty() ) { - // This is not - if ( preferredVersion.startsWith( "1.0" ) ) - { - versions << "VERSION=" + preferredVersion; - } - else if ( preferredVersion.startsWith( "1.1" ) ) - { - // Ignored by UMN Mapserver 6.0.3, see below - versions << "AcceptVersions=" + preferredVersion; - } + versions << preferredVersion; } else { - // 1.0.0 - VERSION - // 1.1.0 - AcceptVersions (not supported by UMN Mapserver 6.0.3 - defaults to latest 1.1 // We prefer 1.0 because 1.1 has many issues, each server implements it in defferent // way with various particularities // It may happen that server supports 1.1.0 but gives error for 1.1 @@ -232,12 +247,7 @@ bool QgsWcsCapabilities::retrieveServerCapabilities( QString preferredVersion ) { clear(); - QString url = prepareUri( mUri.param( "url" ) ) + "SERVICE=WCS&REQUEST=GetCapabilities"; - - if ( !preferredVersion.isEmpty() ) - { - url += "&" + preferredVersion; - } + QString url = getCapabilitiesUrl( preferredVersion ); if ( ! sendRequest( url ) ) { return false; } @@ -261,6 +271,23 @@ bool QgsWcsCapabilities::retrieveServerCapabilities( QString preferredVersion ) return true; } +QString QgsWcsCapabilities::getDescribeCoverageUrl( QString const &identifier ) const +{ + QString url = prepareUri( mUri.param( "url" ) ) + "SERVICE=WCS&REQUEST=DescribeCoverage&VERSION=" + mVersion; + + if ( mVersion.startsWith( "1.0" ) ) + { + url += "&COVERAGE=" + identifier; + } + else if ( mVersion.startsWith( "1.1" ) ) + { + // in 1.1.0, 1.1.1, 1.1.2 the name of param is 'identifier' + // but in KVP 'identifiers' + url += "&IDENTIFIERS=" + identifier; + } + return url; +} + bool QgsWcsCapabilities::describeCoverage( QString const &identifier, bool forceRefresh ) { QgsDebugMsg( " identifier = " + identifier ); @@ -274,18 +301,7 @@ bool QgsWcsCapabilities::describeCoverage( QString const &identifier, bool force if ( coverage->described && ! forceRefresh ) return true; - QString url = prepareUri( mUri.param( "url" ) ) + "SERVICE=WCS&REQUEST=DescribeCoverage&VERSION=" + mVersion; - - if ( mVersion.startsWith( "1.0" ) ) - { - url += "&COVERAGE=" + coverage->identifier; - } - else if ( mVersion.startsWith( "1.1" ) ) - { - // in 1.1.0, 1.1.1, 1.1.2 the name of param is 'identifier' - // but in KVP 'identifiers' - url += "&IDENTIFIERS=" + coverage->identifier; - } + QString url = getDescribeCoverageUrl( coverage->identifier ); if ( ! sendRequest( url ) ) { return false; } diff --git a/src/providers/wcs/qgswcscapabilities.h b/src/providers/wcs/qgswcscapabilities.h index 9c9043ed2311..c5e066b0cb0c 100644 --- a/src/providers/wcs/qgswcscapabilities.h +++ b/src/providers/wcs/qgswcscapabilities.h @@ -125,8 +125,17 @@ class QgsWcsCapabilities : public QObject */ static QString prepareUri( QString uri ); - /**Returns the GetCoverage url - */ + /** \brief Returns the GetCoverage full url + * \param version optional version, e.g. 1.0.0 or 1.1.0 */ + QString getCapabilitiesUrl( const QString version ) const; + + /** \brief Returns the GetCoverage full url using current version */ + QString getCapabilitiesUrl() const; + + /** \brief Returns the GetCoverage full full url using current version */ + QString getDescribeCoverageUrl( QString const &identifier ) const; + + /** Returns the GetCoverage base url */ QString getCoverageUrl() const; //! Send request to server @@ -225,7 +234,7 @@ class QgsWcsCapabilities : public QObject /** * \brief Retrieve and parse the (cached) Capabilities document from the server * - * \param preferredVersion - optional version KVP + * \param preferredVersion - optional version, e.g. 1.0.0, 1.1.0 * * \retval false if the capabilities document could not be retrieved or parsed - * see lastError() for more info diff --git a/tests/src/providers/CMakeLists.txt b/tests/src/providers/CMakeLists.txt index a23f3cfd4e37..f27aa77fac78 100644 --- a/tests/src/providers/CMakeLists.txt +++ b/tests/src/providers/CMakeLists.txt @@ -96,6 +96,7 @@ IF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/raster ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/providers/wcs + ${QT_QTSCRIPT_INCLUDE_DIR} ) TARGET_LINK_LIBRARIES(qgis_wcstest @@ -105,6 +106,7 @@ IF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) ${QT_QTXML_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${QT_QTMAIN_LIBRARY} + ${QT_QTSCRIPT_LIBRARY} qgis_core ) @@ -112,5 +114,6 @@ IF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) BUNDLE DESTINATION ${QGIS_BIN_DIR} RUNTIME DESTINATION ${QGIS_BIN_DIR} ) + INSTALL(FILES wcs-servers.json DESTINATION ${QGIS_DATA_DIR}/resources) ENDIF(UNIX AND NOT ANDROID AND CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/tests/src/providers/testqgswcspublicservers.cpp b/tests/src/providers/testqgswcspublicservers.cpp index a99bd13a247e..3727973536de 100644 --- a/tests/src/providers/testqgswcspublicservers.cpp +++ b/tests/src/providers/testqgswcspublicservers.cpp @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include @@ -46,11 +49,12 @@ int _fmode = _O_BINARY; #include #endif -TestQgsWcsPublicServers::TestQgsWcsPublicServers( const QString & cacheDirPath, int maxCoverages, const QString & server, const QString & coverage, bool force ): +TestQgsWcsPublicServers::TestQgsWcsPublicServers( const QString & cacheDirPath, int maxCoverages, const QString & server, const QString & coverage, const QString &version, bool force ): mCacheDirPath( cacheDirPath ) , mMaxCoverages( maxCoverages ) , mServer( server ) , mCoverage( coverage ) + , mVersion( version ) , mForce( force ) { @@ -83,56 +87,123 @@ void TestQgsWcsPublicServers::init() mHead << "Values"; mHead << "Colors"; mHead << "Has size"; + + // read servers + issues list + QString path = QgsApplication::pkgDataPath() + "/resources/wcs-servers.json"; + QFile file( path ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + QString data = file.readAll(); + //QgsDebugMsg("servers: \n" + str ); + file.close(); + QScriptEngine engine; + QScriptValue result = engine.evaluate( data ); + + QScriptValueIterator serverIt( result ); + while ( serverIt.hasNext() ) + { + serverIt.next(); + QScriptValue serverValue = serverIt.value(); + + QString serverUrl = serverValue.property( "url" ).toString(); + QgsDebugMsg( "serverUrl: " + serverUrl ); + + Server server( serverUrl ); + + QScriptValue issuesValue = serverValue.property( "issues" ); + + QScriptValueIterator issuesIt( issuesValue ); + while ( issuesIt.hasNext() ) + { + issuesIt.next(); + QScriptValue issueValue = issuesIt.value(); + + QString description = issueValue.property( "description" ).toString(); + QgsDebugMsg( "description: " + description ); + Issue issue( description ); + + QScriptValue coveragesValue = issueValue.property( "coverages" ); + QScriptValueIterator coveragesIt( coveragesValue ); + while ( coveragesIt.hasNext() ) + { + coveragesIt.next(); + issue.coverages << coveragesIt.value().toString(); + } + + QScriptValue versionsValue = issueValue.property( "versions" ); + QScriptValueIterator versionsIt( versionsValue ); + while ( versionsIt.hasNext() ) + { + versionsIt.next(); + issue.versions << versionsIt.value().toString(); + } + + server.issues << issue; + } + + mServers << server; + } + } + else + { + QgsDebugMsg( "Cannot open " + path ); + } +} + +QStringList TestQgsWcsPublicServers::issueDescriptions( const QString & url, const QString & coverage, const QString &version ) +{ + QStringList descriptions; + foreach ( Server server, mServers ) + { + if ( server.url == url ) + { + foreach ( Issue issue, server.issues ) + { + if (( issue.coverages.size() == 0 || issue.coverages.contains( coverage ) ) && + ( issue.versions.size() == 0 || issue.versions.contains( version ) ) ) + { + descriptions << issue.description; + } + } + } + } + return descriptions; } void TestQgsWcsPublicServers::test( ) { QStringList versions; + QStringList serverUrls; + // It may happen that server supports 1.1.1, but does not accept 1.1 (http://zeus.pin.unifi.it/gi-wcs/http) - versions << "" << "1.0.0" << "1.1.0"; // empty for default + + if ( !mVersion.isEmpty() ) + { + versions << mVersion; + } + else + { + versions << "" << "1.0.0" << "1.1.0"; // empty for default + } + if ( !mServer.isEmpty() ) { - Server server( mServer ); - mServers << server; + serverUrls << mServer; } else { - // Some (first) coverages do not advertize any supportedCRS and sever gives - // error both with native CRS (EPSG::561005) and EPSG:4326 - // MOD* coverages work OK - mServers << Server( "http://argon.geogr.uni-jena.de:8080/geoserver/ows" ); - mServers << Server( "http://demo.geonode.org/geoserver/wcs" ); - mServers << Server( "http://demo.mapserver.org/cgi-bin/wcs" ); - mServers << Server( "http://demo.opengeo.org/geoserver/wcs" ); - // geobrain.laits.gmu.edu servers are quite slow - mServers << Server( "http://geobrain.laits.gmu.edu/cgi-bin/gbwcs-dem" ); - //mServers << Server ( "http://geobrain.laits.gmu.edu/cgi-bin/ows8/wcseo" ); - //mServers << Server ( "http://geobrain.laits.gmu.edu/cgi-bin/wcs110" ); - //mServers << Server ( "http://geobrain.laits.gmu.edu/cgi-bin/wcs-all" ); - //mServers << Server ( "http://ws.csiss.gmu.edu/cgi-bin/wcs-t" ); - // Big and slow - //mServers << Server ( "http://ws.laits.gmu.edu/cgi-bin/wcs-all" ); - // Slow - //mServers << Server ( "http://iceds.ge.ucl.ac.uk/cgi-bin/icedswcs" ); - mServers << Server( "http://motherlode.ucar.edu:8080/thredds/wcs/fmrc/NCEP/DGEX/Alaska_12km/NCEP-DGEX-Alaska_12km_best.ncd" ); - mServers << Server( "http://navigator.state.or.us/ArcGIS/services/Framework/Imagery_Mosaic2009/ImageServer/WCSServer" ); - mServers << Server( "http://nsidc.org/cgi-bin/atlas_north" ); - // Slow - //mServers << Server ( "http://sedac.ciesin.columbia.edu/geoserver/wcs" ); - // Big and slow - //mServers << Server ( "http://webmap.ornl.gov/ogcbroker/wcs" ); - // Currently very slow or down - //mServers << Server ( "http://www.sogeo.ch/geoserver/wcs" ); - // Slow and erroneous - //mServers << Server ( "http://zeus.pin.unifi.it/gi-wcs/http" ); + foreach ( Server server, mServers ) + { + serverUrls << server.url; + } } - foreach ( Server server, mServers ) + foreach ( QString serverUrl, serverUrls ) { QStringList myServerLog; - myServerLog << "server:" + server.url; - QString myServerDirName = server.url; + myServerLog << "server:" + serverUrl; + QString myServerDirName = serverUrl; myServerDirName.replace( QRegExp( "[:/]+" ), "." ); myServerDirName.replace( QRegExp( "\\.$" ), "" ); QgsDebugMsg( "myServerDirName = " + myServerDirName ); @@ -148,7 +219,7 @@ void TestQgsWcsPublicServers::test( ) foreach ( QString version, versions ) { - QgsDebugMsg( "server: " + server.url + " version: " + version ); + QgsDebugMsg( "server: " + serverUrl + " version: " + version ); QStringList myVersionLog; myVersionLog << "version:" + version; @@ -165,7 +236,7 @@ void TestQgsWcsPublicServers::test( ) QgsDataSourceURI myServerUri; - myServerUri.setParam( "url", server.url ); + myServerUri.setParam( "url", serverUrl ); if ( !version.isEmpty() ) { myServerUri.setParam( "version", version ); @@ -174,6 +245,7 @@ void TestQgsWcsPublicServers::test( ) QgsWcsCapabilities myCapabilities; myCapabilities.setUri( myServerUri ); + if ( !myCapabilities.lastError().isEmpty() ) { QgsDebugMsg( myCapabilities.lastError() ); @@ -181,6 +253,8 @@ void TestQgsWcsPublicServers::test( ) continue; } + myVersionLog << "getCapabilitiesUrl:" + myCapabilities.getCapabilitiesUrl(); + QVector myCoverages; if ( !myCapabilities.supportedCoverages( myCoverages ) ) { @@ -238,7 +312,7 @@ void TestQgsWcsPublicServers::test( ) myUri.setParam( "time", myCoverage.times.value( 0 ) ); } myLog << "version:" + version; - myLog << "uri:" + myUri.encodedUri(); + myLog << "describeCoverageUrl:" + myCapabilities.getDescribeCoverageUrl( myCoverage.identifier ); // Test time //myLog << "date:" + QString( "%1").arg( QDateTime::currentDateTime().toTime_t() ); myLog << "date:" + QString( "%1" ).arg( QDateTime::currentDateTime().toString() ); @@ -405,7 +479,7 @@ void TestQgsWcsPublicServers::report() QDir myVersionDir( myVersionDirPath ); QString myVersion = myVersionLog.value( "version" ); - myServerReport += QString( "

Version: %1

" ).arg( myVersion.isEmpty() ? "(empty)" : myVersion ); + myServerReport += QString( "

Version: %2

" ).arg( myVersionLog.value( "getCapabilitiesUrl" ) ).arg( myVersion.isEmpty() ? "(empty)" : myVersion ); if ( !myVersionLog.value( "error" ).isEmpty() ) { @@ -433,13 +507,15 @@ void TestQgsWcsPublicServers::report() QString myLogPath = myVersionDir.absolutePath() + QDir::separator() + myLogFileName; QMapmyLog = readLog( myLogPath ); QStringList myValues; - myValues << myLog.value( "identifier" ); + myValues << QString( "%2" ).arg( myLog.value( "describeCoverageUrl" ) ).arg( myLog.value( "identifier" ) ); myValues << myLog.value( "version" ); QString imgPath = myVersionDir.absolutePath() + QDir::separator() + QFileInfo( myLogPath ).completeBaseName() + ".png"; if ( !myLog.value( "error" ).isEmpty() ) { myValues << myLog.value( "error" ); + QStringList issues = issueDescriptions( myServerLog.value( "server" ), myLog.value( "identifier" ), myLog.value( "version" ) ); + myValues << issues.join( "
" ); myVersionReport += row( myValues, "cellerr" ); myVersionErrCount++; } @@ -473,10 +549,10 @@ void TestQgsWcsPublicServers::report() myVersionReport += row( myValues, cls ); } } // coverages - myVersionReport += ""; + myVersionReport += "\n"; // prepend counts - myVersionReport.prepend( QString( "Coverages: %1
" ).arg( myVersionCoverageCount ) + - QString( "Errors: %1
" ).arg( myVersionErrCount ) + + myVersionReport.prepend( QString( "Coverages: %1
\n" ).arg( myVersionCoverageCount ) + + QString( "Errors: %1
\n" ).arg( myVersionErrCount ) + QString( "Warnings: %1

" ).arg( myVersionWarnCount ) ); myServerReport += myVersionReport; } @@ -504,12 +580,12 @@ void TestQgsWcsPublicServers::report() myRep += ""; myRep += QString( "

Tested first %1 coverages for each server/version

" ).arg( mMaxCoverages ); - myRep += QString( "Servers: %1
" ).arg( myServerCount ); - myRep += QString( "Servers with error: %1
" ).arg( myServerErrCount ); - myRep += QString( "Servers with warning: %1
" ).arg( myServerWarnCount ); - myRep += QString( "Coverages: %1
" ).arg( myCoverageCount ); - myRep += QString( "Coverage errors: %1
" ).arg( myCoverageErrCount ); - myRep += QString( "Coverage warnings: %1
" ).arg( myCoverageWarnCount ); + myRep += QString( "Servers: %1
\n" ).arg( myServerCount ); + myRep += QString( "Servers with error: %1
\n" ).arg( myServerErrCount ); + myRep += QString( "Servers with warning: %1
\n" ).arg( myServerWarnCount ); + myRep += QString( "Coverages: %1
\n" ).arg( myCoverageCount ); + myRep += QString( "Coverage errors: %1
\n" ).arg( myCoverageErrCount ); + myRep += QString( "Coverage warnings: %1
\n" ).arg( myCoverageWarnCount ); myRep += myReport; @@ -555,7 +631,7 @@ QString TestQgsWcsPublicServers::row( QStringList theValues, QString theClass ) } myRow += QString( "%3" ).arg( theClass ).arg( colspan ).arg( val ); } - myRow += ""; + myRow += "\n"; return myRow; } @@ -566,9 +642,11 @@ void usage( std::string const & appName ) << "Console application for QGIS WCS provider (WCS client) testing.\n" << "Usage: " << appName << " [options] CACHE_DIR\n" << " options: \n" - << "\t[--server URL]\tWCS server URL to be tested\n" - << "\t[--coverage coverage]\tCoverage name to be tested\n" + << "\t[--server URL]\tWCS server URL to be tested.\n" + << "\t[--coverage coverage]\tCoverage name to be tested.\n" << "\t[--num count]\tMaximum number of coverages to test per server. Default 2.\n" + << "\t[--version version]\tWCS version to be tested.\n" + << "\t[--force]\tForce retrieve, overwrite cache.\n" << " FILES:\n" << " Path to directory where cached results are stored.\n" << " Coverage once retrieved (success or fail) is not requested again until the cache is deleted.\n"; @@ -586,6 +664,7 @@ int main( int argc, char *argv[] ) QString myServer; QString myCoverage; + QString myVersion; int myMaxCoverages = 2; bool myForce; @@ -597,6 +676,7 @@ int main( int argc, char *argv[] ) {"server", required_argument, 0, 's'}, {"coverage", required_argument, 0, 'c'}, {"num", required_argument, 0, 'n'}, + {"version", required_argument, 0, 'v'}, {"force", no_argument, 0, 'f'}, {0, 0, 0, 0} }; @@ -606,7 +686,7 @@ int main( int argc, char *argv[] ) /* getopt_long stores the option index here. */ int option_index = 0; - optionChar = getopt_long( argc, argv, "hscnf", + optionChar = getopt_long( argc, argv, "hscnvf", long_options, &option_index ); /* Detect the end of the options. */ @@ -637,6 +717,10 @@ int main( int argc, char *argv[] ) myMaxCoverages = QString( optarg ).toInt(); break; + case 'v': + myVersion = QString( optarg ); + break; + case 'f': myForce = true; break; @@ -656,6 +740,7 @@ int main( int argc, char *argv[] ) QgsDebugMsg( QString( "myServer = %1" ).arg( myServer ) ); QgsDebugMsg( QString( "myCoverage = %1" ).arg( myCoverage ) ); QgsDebugMsg( QString( "myMaxCoverages = %1" ).arg( myMaxCoverages ) ); + QgsDebugMsg( QString( "myVersion = %1" ).arg( myVersion ) ); if ( !myCoverage.isEmpty() && myServer.isEmpty() ) { @@ -691,7 +776,7 @@ int main( int argc, char *argv[] ) QgsApplication::init( QString() ); QgsApplication::initQgis(); - TestQgsWcsPublicServers myTest( myCacheDirPath, myMaxCoverages, myServer, myCoverage, myForce ); + TestQgsWcsPublicServers myTest( myCacheDirPath, myMaxCoverages, myServer, myCoverage, myVersion, myForce ); myTest.init(); myTest.test(); myTest.report(); diff --git a/tests/src/providers/testqgswcspublicservers.h b/tests/src/providers/testqgswcspublicservers.h index 33a1ca0c804b..4cca2b21a994 100644 --- a/tests/src/providers/testqgswcspublicservers.h +++ b/tests/src/providers/testqgswcspublicservers.h @@ -38,6 +38,7 @@ class TestQgsWcsPublicServers: public QObject QStringList versions; // version regex QStringList coverages; // coverage regex QString description; // problem description + Issue( const QString & d ) : description( d ) {} }; struct Server { @@ -48,7 +49,7 @@ class TestQgsWcsPublicServers: public QObject }; - TestQgsWcsPublicServers( const QString & cacheDirPath, int maxCoverages, const QString & server = QString(), const QString & coverage = QString(), bool force = false ); + TestQgsWcsPublicServers( const QString & cacheDirPath, int maxCoverages, const QString & server = QString(), const QString & coverage = QString(), const QString &version = QString(), bool force = false ); void init(); void test(); @@ -60,6 +61,8 @@ class TestQgsWcsPublicServers: public QObject QMap readLog( QString theFileName ); + QStringList issueDescriptions( const QString & url, const QString & coverage, const QString &version ); + QString mCacheDirPath; QDir mCacheDir; @@ -68,6 +71,7 @@ class TestQgsWcsPublicServers: public QObject QString mServer; QString mCoverage; + QString mVersion; // Force cached bool mForce; diff --git a/tests/src/providers/wcs-servers.json b/tests/src/providers/wcs-servers.json new file mode 100644 index 000000000000..e1099f069112 --- /dev/null +++ b/tests/src/providers/wcs-servers.json @@ -0,0 +1,59 @@ +[ + { + url: 'http://demo.opengeo.org/geoserver/wcs', + issues: [ + { + coverages: [ 'og:0' ], + versions: [ ], + description: 'Server fails on DescribeCoverage with: java.io.IOException null Translator error Unexpected error occurred during describe coverage xml encoding ...' + } + ] + }, { + url: 'http://demo.geonode.org/geoserver/wcs' + }, { + url: 'http://demo.mapserver.org/cgi-bin/wcs' + }, { + // Some (first) coverages do not advertise any supportedCRS and sever gives + // error both with native CRS (EPSG::561005) and EPSG:4326 + // MOD* coverages work OK + url: 'http://argon.geogr.uni-jena.de:8080/geoserver/ows' + }, { + // geobrain.laits.gmu.edu servers are quite slow + url: 'http://geobrain.laits.gmu.edu/cgi-bin/gbwcs-dem' +/* + }, { + url: 'http://geobrain.laits.gmu.edu/cgi-bin/ows8/wcseo' + }, { + url: 'http://geobrain.laits.gmu.edu/cgi-bin/wcs110' + }, { + url: 'http://geobrain.laits.gmu.edu/cgi-bin/wcs-all' + }, { + url: 'http://ws.csiss.gmu.edu/cgi-bin/wcs-t' + }, { + // Big and slow + url: 'http://ws.laits.gmu.edu/cgi-bin/wcs-all' + }, { + // Slow + url: 'http://iceds.ge.ucl.ac.uk/cgi-bin/icedswcs' +*/ + }, { + url: 'http://motherlode.ucar.edu:8080/thredds/wcs/fmrc/NCEP/DGEX/Alaska_12km/NCEP-DGEX-Alaska_12km_best.ncd' + }, { + url: 'http://navigator.state.or.us/ArcGIS/services/Framework/Imagery_Mosaic2009/ImageServer/WCSServer' + }, { + url: 'http://nsidc.org/cgi-bin/atlas_north' +/* + }, { + // Slow + url: 'http://sedac.ciesin.columbia.edu/geoserver/wcs' + }, { + // Big and slow + url: 'http://webmap.ornl.gov/ogcbroker/wcs' + }, { + // Currently very slow or down + url: 'http://www.sogeo.ch/geoserver/wcs' + }, { + url: 'http://zeus.pin.unifi.it/gi-wcs/http' +*/ + } +]