56 changes: 38 additions & 18 deletions src/core/qgsrasterdataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<QgsRasterInterface::Range> noData );

Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -590,6 +597,11 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
tr( "Cubic" ) << tr( "Mode" ) << tr( "None" ) : QStringList();
}

/** 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 )
{ Q_UNUSED( createOptions ); Q_UNUSED( format ); return QString(); }

signals:
/** Emit a signal to notify of the progress event.
* Emited theProgress is in percents (0.0-100.0) */
Expand All @@ -603,19 +615,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<double> 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<double> mNoDataValue;
QList<double> mSrcNoDataValue;

/** \brief Source nodata value exist */
QList<bool> 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<bool> 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<double> 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 */
Expand Down
3 changes: 2 additions & 1 deletion src/core/raster/qgsrasterchecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 += "</table>\n";

Expand All @@ -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 );
Expand Down
14 changes: 8 additions & 6 deletions src/core/raster/qgsrasterfilewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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] );
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/raster/qgsrasterinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
10 changes: 6 additions & 4 deletions src/core/raster/qgsrasterinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>::quiet_NaN(); }

/** Test if value is nodata for specific band
* @param bandNo band number
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
33 changes: 20 additions & 13 deletions src/core/raster/qgsrasterlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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;
}
Expand Down Expand Up @@ -1234,9 +1234,10 @@ QString QgsRasterLayer::metadata()
myMetadata += tr( "No Data Value" );
myMetadata += "</p>\n";
myMetadata += "<p>";
if ( mValidNoDataValue )
// TODO: all bands
if ( mDataProvider->srcHasNoDataValue( 1 ) )
{
myMetadata += QString::number( mNoDataValue );
myMetadata += QString::number( mDataProvider->srcNoDataValue( 1 ) );
}
else
{
Expand Down Expand Up @@ -1545,7 +1546,7 @@ double QgsRasterLayer::rasterUnitsPerPixel()
return 1;
}


#if 0
void QgsRasterLayer::resetNoDataValue()
{
mNoDataValue = std::numeric_limits<int>::max();
Expand Down Expand Up @@ -1573,7 +1574,7 @@ void QgsRasterLayer::resetNoDataValue()
mValidNoDataValue = mDataProvider->isNoDataValueValid();
}
}

#endif

void QgsRasterLayer::setBlueBandName( QString const & theBandName )
{
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -2228,6 +2229,7 @@ void QgsRasterLayer::setMinimumValue( QString, double, bool )
//legacy method
}

#if 0
void QgsRasterLayer::setNoDataValue( double theNoDataValue )
{
if ( theNoDataValue != mNoDataValue )
Expand All @@ -2246,6 +2248,7 @@ void QgsRasterLayer::setNoDataValue( double theNoDataValue )
#endif
}
}
#endif

void QgsRasterLayer::setRasterShaderFunction( QgsRasterShaderFunction* )
{
Expand Down Expand Up @@ -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 )
{
Expand All @@ -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<QgsRasterInterface::Range> myNoDataRangeList;

QDomNodeList rangeList = bandElement.elementsByTagName( "noDataRange" );
Expand Down Expand Up @@ -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 ) )
{
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -2851,6 +2857,7 @@ double QgsRasterLayer::readValue( void *data, int type, int index )

return mValidNoDataValue ? mNoDataValue : 0.0;
}
#endif

bool QgsRasterLayer::update()
{
Expand Down
14 changes: 7 additions & 7 deletions src/core/raster/qgsrasterlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -553,7 +553,7 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
bool readColorTable( int theBandNumber, QList<QgsColorRampShader::ColorRampItem>* 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 );
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/core/raster/qgsrasterrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
44 changes: 0 additions & 44 deletions src/core/raster/qgsrasterrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface
virtual QList<int> usesBands() const { return QList<int>(); }

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;
Expand All @@ -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
1 change: 1 addition & 0 deletions src/core/raster/qgsrastertransparency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ int QgsRasterTransparency::alphaValue( double theRedValue, double theGreenValue,
return theGlobalTransparency;
}

// TODO: nodata per band
bool QgsRasterTransparency::isEmpty( double nodataValue ) const
{
return (
Expand Down
122 changes: 77 additions & 45 deletions src/gui/qgsrasterformatsaveoptionswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@
#include "qgsrasterformatsaveoptionswidget.h"
#include "qgslogger.h"
#include "qgsdialog.h"

#include "gdal.h"
#include "cpl_string.h"
#include "cpl_conv.h"
#include "cpl_minixml.h"
#include "qgsrasterlayer.h"
#include "qgsproviderregistry.h"

#include <QSettings>
#include <QInputDialog>
Expand All @@ -31,28 +28,16 @@
#include <QMouseEvent>
#include <QMenu>

// todo put this somewhere else - how can we access gdal provider?
char** papszFromStringList( const QStringList& list )
{
char **papszRetList = NULL;
foreach ( QString elem, list )
{
papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
}
return papszRetList;
}

QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::mBuiltinProfiles;

QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* parent, QString format,
QgsRasterFormatSaveOptionsWidget::Type type,
QString provider )
: QWidget( parent ), mFormat( format ), mProvider( provider )
QgsRasterFormatSaveOptionsWidget::Type type, QString provider )
: QWidget( parent ), mFormat( format ), mProvider( provider ), mRasterLayer( 0 )

{
setupUi( this );


setType( type );

if ( mBuiltinProfiles.isEmpty() )
Expand Down Expand Up @@ -104,6 +89,7 @@ QgsRasterFormatSaveOptionsWidget::QgsRasterFormatSaveOptionsWidget( QWidget* par
mOptionsLineEdit->installEventFilter( this );
mOptionsStackedWidget->installEventFilter( this );

updateControls();
updateProfiles();
}

Expand All @@ -114,12 +100,14 @@ QgsRasterFormatSaveOptionsWidget::~QgsRasterFormatSaveOptionsWidget()
void QgsRasterFormatSaveOptionsWidget::setFormat( QString format )
{
mFormat = format;
updateControls();
updateProfiles();
}

void QgsRasterFormatSaveOptionsWidget::setProvider( QString provider )
{
mProvider = provider;
updateControls();
}

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

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

void QgsRasterFormatSaveOptionsWidget::helpOptions()
{
QString message;

if ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" )
{
GDALDriverH myGdalDriver = GDALGetDriverByName( mFormat.toLocal8Bit().constData() );
if ( myGdalDriver )
// get helpCreationOptionsFormat() function ptr for provider
QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( mProvider );
if ( library )
{
// need to serialize xml to get newlines
CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
GDAL_DMD_CREATIONOPTIONLIST, "" ) );
char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
if ( pszFormattedXML )
message = tr( "Create Options:\n\n%1" ).arg( pszFormattedXML );
if ( psCOL )
CPLDestroyXMLNode( psCOL );
if ( pszFormattedXML )
CPLFree( pszFormattedXML );
helpCreationOptionsFormat_t * helpCreationOptionsFormat =
( helpCreationOptionsFormat_t * ) cast_to_fptr( library->resolve( "helpCreationOptionsFormat" ) );
if ( helpCreationOptionsFormat )
{
message = helpCreationOptionsFormat( mFormat );
}
else
{
message = library->fileName() + " does not have helpCreationOptionsFormat";
}
}
else
message = QString( "cannot load provider library %1" ).arg( mProvider );


if ( message.isEmpty() )
message = tr( "Cannot get create options for driver %1" ).arg( mFormat );
}
Expand All @@ -267,36 +263,65 @@ 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 );
textEdit->setText( message );
dlg->layout()->addWidget( textEdit );
dlg->resize( 600, 600 );
dlg->resize( 600, 400 );
dlg->show(); //non modal
}

bool QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui )
QString QgsRasterFormatSaveOptionsWidget::validateOptions( bool gui, bool reportOK )
{
QStringList createOptions = options();
bool ok = false;
QString message;

if ( !createOptions.isEmpty() && mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" )
{
GDALDriverH myGdalDriver = GDALGetDriverByName( mFormat.toLocal8Bit().constData() );
if ( myGdalDriver )
if ( mRasterLayer )
{
QgsDebugMsg( "calling validate on layer's data provider" );
message = mRasterLayer->dataProvider()->validateCreationOptions( createOptions, mFormat );
}
else
{
// print error string?
char** papszOptions = papszFromStringList( createOptions );
ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
CSLDestroy( papszOptions );
if ( gui )
// get validateCreationOptionsFormat() function ptr for provider
QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( mProvider );
if ( library )
{
if ( ok )
QMessageBox::information( this, "", tr( "Valid" ), QMessageBox::Close );
validateCreationOptionsFormat_t * validateCreationOptionsFormat =
( validateCreationOptionsFormat_t * ) cast_to_fptr( library->resolve( "validateCreationOptionsFormat" ) );
if ( validateCreationOptionsFormat )
{
message = validateCreationOptionsFormat( createOptions, mFormat );
}
else
QMessageBox::warning( this, "", tr( "Invalid" ), QMessageBox::Close );
{
message = library->fileName() + " does not have validateCreationOptionsFormat";
}
}
else
message = QString( "cannot load provider library %1" ).arg( mProvider );
}

if ( gui )
{
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 );
}
}
}
return ok;
else
{
QMessageBox::information( this, "", tr( "Cannot validate" ), QMessageBox::Close );
}

return message;
}

void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
Expand Down Expand Up @@ -481,6 +506,13 @@ void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( int newIndex )
updateOptions();
}

void QgsRasterFormatSaveOptionsWidget::updateControls()
{
bool enabled = ( mProvider == "gdal" && mFormat != "" && mFormat != "_pyramids" );
mOptionsValidateButton->setEnabled( enabled );
mOptionsHelpButton->setEnabled( enabled );
}

// map options label left mouse click to optionsToggle()
bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
{
Expand Down
7 changes: 6 additions & 1 deletion src/gui/qgsrasterformatsaveoptionswidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "ui_qgsrasterformatsaveoptionswidgetbase.h"

class QgsRasterLayer;

/** \ingroup gui
* A widget to select format-specific raster saving options
*/
Expand All @@ -46,14 +48,15 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget,

void setFormat( QString format );
void setProvider( QString provider );
void setRasterLayer( QgsRasterLayer* rasterLayer ) { mRasterLayer = rasterLayer; }
QStringList options() const;
void setType( QgsRasterFormatSaveOptionsWidget::Type type = Default );

public slots:

void apply();
void helpOptions();
bool validateOptions( bool gui = true );
QString validateOptions( bool gui = true, bool reportOk = true );
void updateProfiles();

private slots:
Expand All @@ -68,11 +71,13 @@ class GUI_EXPORT QgsRasterFormatSaveOptionsWidget: public QWidget,
void optionsTableEnableDeleteButton();
void updateOptions();
void swapOptionsUI( int newIndex = -1 );
void updateControls();

private:

QString mFormat;
QString mProvider;
QgsRasterLayer* mRasterLayer;
QMap< QString, QString> mOptionsMap;
static QMap< QString, QStringList > mBuiltinProfiles;

Expand Down
12 changes: 12 additions & 0 deletions src/gui/qgsrasterlayersaveasdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer* rasterLa
{
mCreateOptionsWidget->setFormat( myFormats[0] );
}
mCreateOptionsWidget->setRasterLayer( mRasterLayer );
mCreateOptionsWidget->update();
}

Expand Down Expand Up @@ -753,3 +754,14 @@ QgsRasterDataProvider::RasterBuildPyramids QgsRasterLayerSaveAsDialog::buildPyra
return QgsRasterDataProvider::PyramidsFlagYes;
}

bool QgsRasterLayerSaveAsDialog::validate() const
{
if ( mCreateOptionsGroupBox->isChecked() )
{
QString message = mCreateOptionsWidget->validateOptions( true, false );
if ( !message.isNull() )
return false;
}
return true;
}

4 changes: 4 additions & 0 deletions src/gui/qgsrasterlayersaveasdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
void hideFormat();
void hideOutput();

public slots:
virtual void accept() { if ( validate() ) return QDialog::accept(); }

private slots:
void on_mRawModeRadioButton_toggled( bool );
void on_mBrowseButton_clicked();
Expand Down Expand Up @@ -132,6 +135,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
void setNoDataToEdited( int row );
double noDataCellValue( int row, int column ) const;
void adjustNoDataCellWidth( int row, int column );
bool validate() const;
};


Expand Down
184 changes: 171 additions & 13 deletions src/mapserver/qgswfsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,37 @@ QDomDocument QgsWFSServer::describeFeatureType()
//xsd:element
QDomElement geomElem = doc.createElement( "element"/*xsd:element*/ );
geomElem.setAttribute( "name", "geometry" );
geomElem.setAttribute( "type", "gml:GeometryPropertyType" );
QGis::WkbType wkbType = layer->wkbType();
switch ( wkbType )
{
case QGis::WKBPoint25D:
case QGis::WKBPoint:
geomElem.setAttribute( "type", "gml:PointPropertyType" );
break;
case QGis::WKBLineString25D:
case QGis::WKBLineString:
geomElem.setAttribute( "type", "gml:LineStringPropertyType" );
break;
case QGis::WKBPolygon25D:
case QGis::WKBPolygon:
geomElem.setAttribute( "type", "gml:PolygonPropertyType" );
break;
case QGis::WKBMultiPoint25D:
case QGis::WKBMultiPoint:
geomElem.setAttribute( "type", "gml:MultiPointPropertyType" );
break;
case QGis::WKBMultiLineString25D:
case QGis::WKBMultiLineString:
geomElem.setAttribute( "type", "gml:MultiLineStringPropertyType" );
break;
case QGis::WKBMultiPolygon25D:
case QGis::WKBMultiPolygon:
geomElem.setAttribute( "type", "gml:MultiPolygonPropertyType" );
break;
default:
geomElem.setAttribute( "type", "gml:GeometryPropertyType" );
break;
}
geomElem.setAttribute( "minOccurs", "0" );
geomElem.setAttribute( "maxOccurs", "1" );
sequenceElem.appendChild( geomElem );
Expand Down Expand Up @@ -543,9 +573,11 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
}
}

if ( bboxOk )
searchRect.set( minx, miny, maxx, maxy );
QgsCoordinateReferenceSystem layerCrs = layer->crs();

startGetFeature( request, format );
startGetFeature( request, format, layerCrs, &searchRect );

if ( fidOk )
{
Expand Down Expand Up @@ -589,8 +621,6 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
}
else
{
if ( bboxOk )
searchRect.set( minx, miny, maxx, maxy );
provider->select( attrIndexes, searchRect, mWithGeom, true );
while ( provider->nextFeature( feature ) && featureCounter < maxFeat )
{
Expand All @@ -608,25 +638,102 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format
return 0;
}

void QgsWFSServer::startGetFeature( QgsRequestHandler& request, const QString& format )
void QgsWFSServer::startGetFeature( QgsRequestHandler& request, const QString& format, QgsCoordinateReferenceSystem& crs, QgsRectangle* rect )
{
QByteArray result;
QString fcString;
if ( format == "GeoJSON" )
{
fcString = "{\"type\": \"FeatureCollection\",\n";
fcString += " \"bbox\": [ "+ QString::number( rect->xMinimum(), 'f' ) +", "+ QString::number( rect->yMinimum(), 'f' ) +", "+ QString::number( rect->xMaximum(), 'f' ) +", "+ QString::number( rect->yMaximum(), 'f' ) +"],\n";
fcString += " \"features\": [\n";
result = fcString.toUtf8();
request.startGetFeatureResponse( &result, format );
}
else
{
//Prepare url
//Some client requests already have http://<SERVER_NAME> in the REQUEST_URI variable
QString hrefString;
QString requestUrl = getenv( "REQUEST_URI" );
QUrl mapUrl( requestUrl );
mapUrl.setHost( QString( getenv( "SERVER_NAME" ) ) );

//Add non-default ports to url
QString portString = getenv( "SERVER_PORT" );
if ( !portString.isEmpty() )
{
bool portOk;
int portNumber = portString.toInt( &portOk );
if ( portOk )
{
if ( portNumber != 80 )
{
mapUrl.setPort( portNumber );
}
}
}

if ( QString( getenv( "HTTPS" ) ).compare( "on", Qt::CaseInsensitive ) == 0 )
{
mapUrl.setScheme( "https" );
}
else
{
mapUrl.setScheme( "http" );
}

QList<QPair<QString, QString> > queryItems = mapUrl.queryItems();
QList<QPair<QString, QString> >::const_iterator queryIt = queryItems.constBegin();
for ( ; queryIt != queryItems.constEnd(); ++queryIt )
{
if ( queryIt->first.compare( "REQUEST", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
mapUrl.addQueryItem( queryIt->first, "DescribeFeatureType" );
}
else if ( queryIt->first.compare( "FORMAT", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "OUTPUTFORMAT", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "BBOX", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "FEATUREID", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "FILTER", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "MAXFEATURES", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "PROPERTYNAME", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
else if ( queryIt->first.compare( "_DC", Qt::CaseInsensitive ) == 0 )
{
mapUrl.removeQueryItem( queryIt->first );
}
}
mapUrl.addQueryItem( "OUTPUTFORMAT", "XMLSCHEMA" );
hrefString = mapUrl.toString();

//wfs:FeatureCollection
fcString = "<wfs:FeatureCollection";
fcString += " xmlns=\"http://www.opengis.net/wfs\"";
fcString += " xmlns:wfs=\"http://www.opengis.net/wfs\"";
fcString += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
fcString += " xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd\"";
fcString += " xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://www.opengis.net/gml "+ hrefString.replace( "&", "&amp;" ) +"\"";
fcString += " xmlns:ogc=\"http://www.opengis.net/ogc\"";
fcString += " xmlns:gml=\"http://www.opengis.net/gml\"";
fcString += " xmlns:ows=\"http://www.opengis.net/ows\"";
Expand All @@ -635,6 +742,21 @@ void QgsWFSServer::startGetFeature( QgsRequestHandler& request, const QString& f
fcString += ">";
result = fcString.toUtf8();
request.startGetFeatureResponse( &result, format );

QDomDocument doc;
QDomElement bbElem = doc.createElement( "gml:boundedBy" );
QDomElement boxElem = createBoxElem( rect, doc );
if ( !boxElem.isNull() )
{
if ( crs.isValid() )
{
boxElem.setAttribute( "srsName", crs.authid() );
}
bbElem.appendChild( boxElem );
doc.appendChild( bbElem );
}
result = doc.toByteArray();
request.sendGetFeatureResponse( &result );
}
fcString = "";
}
Expand Down Expand Up @@ -701,6 +823,10 @@ QString QgsWFSServer::createFeatureGeoJSON( QgsFeature* feat, QgsCoordinateRefer
QgsGeometry* geom = feat->geometry();
if ( geom && mWithGeom )
{
QgsRectangle box = geom->boundingBox();

fStr += " \"bbox\": [ "+ QString::number( box.xMinimum(), 'f' ) +", "+ QString::number( box.yMinimum(), 'f' ) +", "+ QString::number( box.xMaximum(), 'f' ) +", "+ QString::number( box.yMaximum(), 'f' ) +"],\n";

fStr += " \"geometry\": ";
fStr += geom->exportToGeoJSON();
fStr += ",\n";
Expand Down Expand Up @@ -759,14 +885,25 @@ QDomElement QgsWFSServer::createFeatureElem( QgsFeature* feat, QDomDocument& doc
if ( mWithGeom )
{
//add geometry column (as gml)
QgsGeometry* geom = feat->geometry();

QDomElement geomElem = doc.createElement( "qgs:geometry" );
QDomElement gmlElem = createGeometryElem( feat->geometry(), doc );
QDomElement gmlElem = createGeometryElem( geom, doc );
if ( !gmlElem.isNull() )
{
QgsRectangle box = geom->boundingBox();
QDomElement bbElem = doc.createElement( "gml:boundedBy" );
QDomElement boxElem = createBoxElem( &box, doc );

if ( crs.isValid() )
{
boxElem.setAttribute( "srsName", crs.authid() );
gmlElem.setAttribute( "srsName", crs.authid() );
}

bbElem.appendChild( boxElem );
typeNameElement.appendChild( bbElem );

geomElem.appendChild( gmlElem );
typeNameElement.appendChild( geomElem );
}
Expand All @@ -793,6 +930,27 @@ QDomElement QgsWFSServer::createFeatureElem( QgsFeature* feat, QDomDocument& doc
return featureElement;
}

QDomElement QgsWFSServer::createBoxElem( QgsRectangle* box, QDomDocument& doc ) /*const*/
{
if ( !box )
{
return QDomElement();
}

QDomElement boxElem = doc.createElement( "gml:Box" );
QVector<QgsPoint> v;
QgsPoint p1;
p1.set( box->xMinimum(), box->yMinimum() );
v.append( p1 );
QgsPoint p2;
p2.set( box->xMaximum(), box->yMaximum() );
v.append( p2 );
QDomElement coordElem = createCoordinateElem( v, doc );
boxElem.appendChild( coordElem );

return boxElem;
}

QDomElement QgsWFSServer::createGeometryElem( QgsGeometry* geom, QDomDocument& doc ) /*const*/
{
if ( !geom )
Expand Down Expand Up @@ -931,13 +1089,13 @@ QDomElement QgsWFSServer::createPolygonElem( QgsGeometry* geom, QDomDocument& do
QString boundaryName;
if ( i == 0 )
{
boundaryName = "outerBoundaryIs";
boundaryName = "gml:outerBoundaryIs";
}
else
{
boundaryName = "innerBoundaryIs";
boundaryName = "gml:innerBoundaryIs";
}
QDomElement boundaryElem = doc.createElementNS( "http://www.opengis.net/gml", boundaryName );
QDomElement boundaryElem = doc.createElement( boundaryName );
QDomElement ringElem = doc.createElement( "gml:LinearRing" );
QDomElement coordElem = createCoordinateElem( poly.at( i ), doc );
ringElem.appendChild( coordElem );
Expand Down Expand Up @@ -979,7 +1137,7 @@ QDomElement QgsWFSServer::createCoordinateElem( const QVector<QgsPoint> points,
coordElem.setAttribute( "ts", " " );

//precision 4 for meters / feet, precision 8 for degrees
int precision = 8;
int precision = 6;
/*
if ( mSourceCRS.mapUnits() == QGis::Meters
|| mSourceCRS.mapUnits() == QGis::Feet )
Expand All @@ -996,9 +1154,9 @@ QDomElement QgsWFSServer::createCoordinateElem( const QVector<QgsPoint> points,
{
coordString += " ";
}
coordString += QString::number( pointIt->x(), 'f', precision );
coordString += QString::number( pointIt->x(), 'f');
coordString += ",";
coordString += QString::number( pointIt->y(), 'f', precision );
coordString += QString::number( pointIt->y(), 'f' );
}

QDomText coordText = doc.createTextNode( coordString );
Expand Down
4 changes: 3 additions & 1 deletion src/mapserver/qgswfsserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
class QgsField;
class QgsFeature;
class QgsRectangle;
class QgsGeometry;
class QgsSymbol;
class QgsRequestHandler;
Expand Down Expand Up @@ -80,7 +81,7 @@ class QgsWFSServer

protected:

void startGetFeature( QgsRequestHandler& request, const QString& format );
void startGetFeature( QgsRequestHandler& request, const QString& format, QgsCoordinateReferenceSystem& crs, QgsRectangle* rect );
void sendGetFeature( QgsRequestHandler& request, const QString& format, QgsFeature* feat, int featIdx, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes );
void endGetFeature( QgsRequestHandler& request, const QString& format );

Expand All @@ -90,6 +91,7 @@ class QgsWFSServer
//methods to write GML2
QDomElement createFeatureElem( QgsFeature* feat, QDomDocument& doc, QgsCoordinateReferenceSystem& crs, QMap< int, QgsField > fields, QSet<QString> hiddenAttributes ) /*const*/;

QDomElement createBoxElem( QgsRectangle* box, QDomDocument& doc ) /* const */;
QDomElement createGeometryElem( QgsGeometry* g, QDomDocument& doc ) /*const*/;
QDomElement createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
QDomElement createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
Expand Down
184 changes: 144 additions & 40 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
}
#endif

#if 0
bool QgsGdalProvider::srcHasNoDataValue( int bandNo ) const
{
if ( mGdalDataset )
Expand All @@ -754,6 +755,7 @@ double QgsGdalProvider::noDataValue() const
}
return std::numeric_limits<int>::max(); // should not happen or be used
}
#endif

void QgsGdalProvider::computeMinMax( int theBandNo )
{
Expand Down Expand Up @@ -834,7 +836,7 @@ QMap<int, void *> 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 );
}
}
Expand Down Expand Up @@ -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 ) );
Expand Down Expand Up @@ -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<int>::max();
// Would NaN work well?
//myNoDataValue = std::numeric_limits<double>::quiet_NaN();
mGdalDataType.append( myGdalDataType );
}
mSrcNoDataValue.append( std::numeric_limits<double>::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<int>::max();
// NaN should work well
myInternalNoDataValue = std::numeric_limits<double>::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;
Expand Down Expand Up @@ -2450,3 +2458,99 @@ QGISEXTERN void buildSupportedRasterFileFilter( QString & theFileFiltersString )
buildSupportedRasterFileFilterAndExtensions( theFileFiltersString, exts, wildcards );
}

/**
Gets creation options metadata for a given format
*/
QGISEXTERN QString helpCreationOptionsFormat( QString format )
{
QString message;
GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
if ( myGdalDriver )
{
// 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 );
if ( psCOL )
CPLDestroyXMLNode( psCOL );
if ( pszFormattedXML )
CPLFree( pszFormattedXML );
}
return message;
}

/**
Validates creation options for a given format, regardless of layer.
*/
QGISEXTERN QString validateCreationOptionsFormat( const QStringList& createOptions, QString format )
{
GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
if ( ! myGdalDriver )
return "invalid GDAL driver";

char** papszOptions = papszFromStringList( createOptions );
// get error string?
int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
CSLDestroy( papszOptions );

if ( ok == FALSE )
return "Failed GDALValidateCreationOptions() test";
return QString();
}

QString QgsGdalProvider::validateCreationOptions( const QStringList& createOptions, QString format )
{
QString message;

// first validate basic syntax with GDALValidateCreationOptions
message = validateCreationOptionsFormat( createOptions, format );
if ( !message.isNull() )
return message;

// next do specific validations, depending on format and dataset
// only check certain destination formats
QStringList formatsCheck;
formatsCheck << "gtiff";
if ( ! formatsCheck.contains( format.toLower() ) )
return QString();

// prepare a map for easier lookup
QMap< QString, QString > optionsMap;
foreach ( QString option, createOptions )
{
QStringList opt = option.split( "=" );
optionsMap[ opt[0].toUpper()] = opt[1];
QgsDebugMsg( "option: " + option );
}

// gtiff files - validate PREDICTOR option
// see gdal: frmts/gtiff/geotiff.cpp and libtiff: tif_predict.c)
if ( format.toLower() == "gtiff" && optionsMap.contains( "PREDICTOR" ) )
{
QString value = optionsMap.value( "PREDICTOR" );
GDALDataType nDataType = ( mGdalDataType.count() > 0 ) ? ( GDALDataType ) mGdalDataType[ 0 ] : GDT_Unknown;
int nBitsPerSample = nDataType != GDT_Unknown ? GDALGetDataTypeSize( nDataType ) : 0;
QgsDebugMsg( QString( "PREDICTOR: %1 nbits: %2 type: %3" ).arg( value ).arg( nBitsPerSample ).arg(( GDALDataType ) mGdalDataType[ 0 ] ) );
// PREDICTOR=2 only valid for 8/16/32 bits per sample
// TODO check for NBITS option (see geotiff.cpp)
if ( value == "2" )
{
if ( nBitsPerSample != 8 && nBitsPerSample != 16 &&
nBitsPerSample != 32 )
{
message = QString( "PREDICTOR=%1 only valid for 8/16/32 bits per sample (using %2)" ).arg( value ).arg( nBitsPerSample );
}
}
// PREDICTOR=3 only valid for float/double precision
else if ( value == "3" )
{
if ( nDataType != GDT_Float32 && nDataType != GDT_Float64 )
message = "PREDICTOR=3 only valid for float/double precision";
}
}

return message;
}
6 changes: 4 additions & 2 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -285,6 +285,8 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
/**Remove dataset*/
bool remove();

QString validateCreationOptions( const QStringList& createOptions, QString format );

signals:
void statusChanged( QString );

Expand Down
21 changes: 16 additions & 5 deletions src/providers/grass/qgis.d.rast.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef WIN32
#include <fcntl.h>
#include <io.h>
Expand Down Expand Up @@ -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 ) ) );

Expand Down Expand Up @@ -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
Expand Down
25 changes: 12 additions & 13 deletions src/providers/grass/qgsgrassrasterprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* (at your option) any later version. *
* *
***************************************************************************/

#include <limits>

#include "qgslogger.h"
#include "qgsgrass.h"
Expand Down Expand Up @@ -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() );
Expand All @@ -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 )
{
Expand All @@ -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<double>::quiet_NaN();
}
else
{
Expand All @@ -115,9 +117,11 @@ QgsGrassRasterProvider::QgsGrassRasterProvider( QString const & uri )
}

// limit: 3.40282347e+38
mNoDataValue = -1e+30;
//myInternalNoDataValue = -1e+30;
myInternalNoDataValue = std::numeric_limits<float>::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
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -428,7 +427,7 @@ QMap<int, void *> 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" )
{
Expand Down
4 changes: 2 additions & 2 deletions src/providers/grass/qgsgrassrasterprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -263,7 +263,7 @@ class QgsGrassRasterProvider : public QgsRasterDataProvider

QgsGrassRasterValue mRasterValue;

double mNoDataValue;
//double mNoDataValue;
};

#endif
78 changes: 47 additions & 31 deletions src/providers/wcs/qgswcscapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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; }

Expand All @@ -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 );
Expand All @@ -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; }

Expand Down Expand Up @@ -738,7 +754,7 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom10( QByteArray const &xml, QgsW

// may be GTiff, GeoTIFF, TIFF, GIF, ....
coverage->supportedFormat = domElementsTexts( coverageOfferingElement, "supportedFormats.formats" );
//QgsDebugMsg( "supportedFormat = " + coverage->supportedFormat.join( "," ) );
QgsDebugMsg( "supportedFormat = " + coverage->supportedFormat.join( "," ) );

// spatialDomain and Grid/RectifiedGrid are optional according to specificationi.
// If missing, we cannot get native resolution and size.
Expand Down
15 changes: 12 additions & 3 deletions src/providers/wcs/qgswcscapabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
107 changes: 54 additions & 53 deletions src/providers/wcs/qgswcsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,68 +280,72 @@ 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<int>::max();
// Would NaN work well?
//myNoDataValue = std::numeric_limits<double>::quiet_NaN();
mGdalDataType.append( myGdalDataType );
}
mSrcNoDataValue.append( std::numeric_limits<double>::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<int>::max();
// NaN should work well
myInternalNoDataValue = std::numeric_limits<double>::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 ) )
{
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
Expand Down Expand Up @@ -800,11 +804,17 @@ void QgsWcsProvider::cacheReplyFinished()
QgsDebugMsg( "contentType: " + contentType );

// Exception
// Content type examples: text/xml
// application/vnd.ogc.se_xml;charset=UTF-8
// application/xml
if ( contentType.startsWith( "text/", Qt::CaseInsensitive ) ||
contentType.toLower() == "application/vnd.ogc.se_xml" )
contentType.toLower() == "application/xml" ||
contentType.startsWith( "application/vnd.ogc.se_xml", Qt::CaseInsensitive ) )
{
QByteArray text = mCacheReply->readAll();
if (( contentType.toLower() == "text/xml" || contentType.toLower() == "application/vnd.ogc.se_xml" )
if (( contentType.toLower() == "text/xml" ||
contentType.toLower() == "application/xml" ||
contentType.startsWith( "application/vnd.ogc.se_xml", Qt::CaseInsensitive ) )
&& parseServiceExceptionReportDom( text ) )
{
QgsMessageLog::logMessage( tr( "Map request error (Title:%1; Error:%2; URL: %3)" )
Expand Down Expand Up @@ -1074,15 +1084,6 @@ int QgsWcsProvider::bandCount() const
return mBandCount;
}

double QgsWcsProvider::noDataValue() const
{
if ( mNoDataValue.size() > 0 )
{
return mNoDataValue[0];
}
return std::numeric_limits<int>::max(); // should not happen or be used
}

// this is only called once when statistics are calculated
// TODO
int QgsWcsProvider::xBlockSize() const
Expand Down Expand Up @@ -1525,7 +1526,7 @@ QMap<int, void *> 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;
Expand Down
4 changes: 2 additions & 2 deletions src/providers/wcs/qgswcsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -303,7 +303,7 @@ class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase
QList<int>mSrcGdalDataType;

/** \brief Cell value representing no data. e.g. -9999, indexed from 0 */
QList<double> mNoDataValue;
//QList<double> mNoDataValue;

/** Color tables indexed from 0 */
QList< QList<QgsColorRampShader::ColorRampItem> > mColorTables;
Expand Down
2 changes: 1 addition & 1 deletion src/providers/wfs/qgswfsutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class QgsExpressionOGCVisitor : public QgsExpression::Visitor
case QgsExpression::boEQ: opName = "PropertyIsEqualTo"; break;
case QgsExpression::boNE: opName = "PropertyIsNotEqualTo"; break;
case QgsExpression::boLE: opName = "PropertyIsLessThanOrEqualTo"; break;
case QgsExpression::boGE: opName = "PropertyIsLessThanOrEqualTo"; break;
case QgsExpression::boGE: opName = "PropertyIsGreaterThanOrEqualTo"; break;
case QgsExpression::boLT: opName = "PropertyIsLessThan"; break;
case QgsExpression::boGT: opName = "PropertyIsGreaterThan"; break;
case QgsExpression::boOr: opName = "Or"; break;
Expand Down
7 changes: 5 additions & 2 deletions src/ui/qgsrasterlayerpropertiesbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -439,9 +439,12 @@
<number>1</number>
</property>
<item>
<widget class="QLabel" name="lblSrcNoDataValueLabel">
<widget class="QCheckBox" name="mSrcNoDataValueCheckBox">
<property name="toolTip">
<string>Use original source no data value.</string>
</property>
<property name="text">
<string>Source no data value:</string>
<string>No data value:</string>
</property>
</widget>
</item>
Expand Down
2 changes: 1 addition & 1 deletion tests/src/core/testqgsrasterfilewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
3 changes: 3 additions & 0 deletions tests/src/providers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -105,12 +106,14 @@ 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
)

INSTALL (TARGETS qgis_wcstest
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)
529 changes: 378 additions & 151 deletions tests/src/providers/testqgswcspublicservers.cpp

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion tests/src/providers/testqgswcspublicservers.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,48 @@ class TestQgsWcsPublicServers: public QObject
// Known problem
struct Issue
{
QString offender; // server or empty == qgis
QStringList versions; // version regex
QStringList coverages; // coverage regex
QString description; // problem description
Issue( const QString & d ) : description( d ) {}
};
struct Server
{
Server( ) {}
Server( const QString & u ) : url( u ) {}
QString url; // URL
QString description; // notes
QList<TestQgsWcsPublicServers::Issue> issues;
};

enum OffenderType
{
NoOffender = 0,
ServerOffender = 1,
QGisOffender = 1 << 1
};

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();
void report();
private:
QString cells( QStringList theValues, QString theClass = QString(), int colspan = 1 );
QString row( QStringList theValues, QString theClass = QString() );
QString error( QString theMessage );
void writeReport( QString theReport );

QMap<QString, QString> readLog( QString theFileName );

Server getServer( const QString & url );

QList<Issue> issues( const QString & url, const QString & coverage, const QString &version );
QStringList issueDescriptions( const QString & url, const QString & coverage, const QString &version );

int issueOffender( const QString & url, const QString & coverage, const QString &version );

QString mCacheDirPath;
QDir mCacheDir;

Expand All @@ -68,6 +85,7 @@ class TestQgsWcsPublicServers: public QObject

QString mServer;
QString mCoverage;
QString mVersion;

// Force cached
bool mForce;
Expand Down
81 changes: 81 additions & 0 deletions tests/src/providers/wcs-servers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
[
{
url: 'http://demo.opengeo.org/geoserver/wcs',
description: 'Does not work at all with gvSIG-1_11-1305-final.',
issues: [
{
offender: 'server',
coverages: [ 'og:0' ],
versions: [ '1.0.0' ],
description: 'The server fails in DescribeCoverage with: java.io.IOException null Translator error Unexpected error occurred during describe coverage xml encoding ...'
},{
offender: 'server',
coverages: [ 'bm' ],
versions: [ '1.1.0' ],
description: 'The server fails in DescribeCoverage with: java.io.IOException null Translator error Unexpected error occurred during describe coverage xml encoding ...'
},{
offender: 'server',
coverages: [ 'usgs:nlcd', 'nlcd' ],
versions: [ '1.0.0', '1.1.0' ],
description: 'The server does no offer any CRS in DescribeCoverage supportedCRSs / supportedCRS. QGIS tries to get coverage using EPSG:5070, in which the coverage spatialDomain.Envelope is defined, but server fails reporting error: Could not recognize crs ...'
},{
offender: 'server',
coverages: [ '0', 'naturalearth' ],
versions: [ '1.1.0' ],
description: "The server fails in GetCoverage with 'java.lang.IllegalArgumentException: xScale:the parameter value is not valid. xScale:the parameter value is not valid.' It fails with BOUNDINGBOX=-2,-6,2,6&GRIDORIGIN=2,-6&GRIDOFFSETS=-2,3 but works with BOUNDINGBOX=-2,-6,2,6&GRIDORIGIN=2,-6&GRIDOFFSETS=2,-3 (GRIDOFFSETS signs switched) other coverages (Arc_Sample e.g.) work OK with the first"
},{
offender: 'server',
coverages: [ 'Img_Sample' ],
versions: [ '1.1.0' ],
description: "The server fails in GetCoverage with 'java.lang.IllegalArgumentException: The specified dimensional parameter is non-positive. The specified dimensional parameter is non-positive'."
}
]
}, {
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'
*/
}
]