198 changes: 191 additions & 7 deletions src/core/qgsrasterdataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,31 +201,31 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo )
double d;
switch ( type )
{
case QgsRasterDataProvider::Byte:
case Byte:
uc = ( unsigned char )noval;
memcpy( data, &uc, size );
break;
case QgsRasterDataProvider::UInt16:
case UInt16:
us = ( unsigned short )noval;
memcpy( data, &us, size );
break;
case QgsRasterDataProvider::Int16:
case Int16:
s = ( short )noval;
memcpy( data, &s, size );
break;
case QgsRasterDataProvider::UInt32:
case UInt32:
ui = ( unsigned int )noval;
memcpy( data, &ui, size );
break;
case QgsRasterDataProvider::Int32:
case Int32:
i = ( int )noval;
memcpy( data, &i, size );
break;
case QgsRasterDataProvider::Float32:
case Float32:
f = ( float )noval;
memcpy( data, &f, size );
break;
case QgsRasterDataProvider::Float64:
case Float64:
d = ( double )noval;
memcpy( data, &d, size );
break;
Expand All @@ -235,4 +235,188 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo )
return ba;
}


QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
{
double myNoDataValue = noDataValue();
QgsRasterBandStats myRasterBandStats;
myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later

int myDataType = dataType( theBandNo );

int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
myXBlockSize = xBlockSize();
myYBlockSize = yBlockSize();

myNXBlocks = ( xSize() + myXBlockSize - 1 ) / myXBlockSize;
myNYBlocks = ( ySize() + myYBlockSize - 1 ) / myYBlockSize;

void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) );

// unfortunately we need to make two passes through the data to calculate stddev
bool myFirstIterationFlag = true;

int myBandXSize = xSize();
int myBandYSize = ySize();
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
{
for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
{
int nXValid, nYValid;
readBlock( theBandNo, iXBlock, iYBlock, myData );

// Compute the portion of the block that is valid
// for partial edge blocks.
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
nXValid = myBandXSize - iXBlock * myXBlockSize;
else
nXValid = myXBlockSize;

if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
nYValid = myBandYSize - iYBlock * myYBlockSize;
else
nYValid = myYBlockSize;

// Collect the histogram counts.
for ( int iY = 0; iY < nYValid; iY++ )
{
for ( int iX = 0; iX < nXValid; iX++ )
{
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) );

if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
{
continue; // NULL
}

myRasterBandStats.sum += myValue;
++myRasterBandStats.elementCount;
//only use this element if we have a non null element
if ( myFirstIterationFlag )
{
//this is the first iteration so initialise vars
myFirstIterationFlag = false;
myRasterBandStats.minimumValue = myValue;
myRasterBandStats.maximumValue = myValue;
} //end of true part for first iteration check
else
{
//this is done for all subsequent iterations
if ( myValue < myRasterBandStats.minimumValue )
{
myRasterBandStats.minimumValue = myValue;
}
if ( myValue > myRasterBandStats.maximumValue )
{
myRasterBandStats.maximumValue = myValue;
}
} //end of false part for first iteration check
}
}
} //end of column wise loop
} //end of row wise loop


//end of first pass through data now calculate the range
myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
//calculate the mean
myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;

//for the second pass we will get the sum of the squares / mean
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
{
for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
{
int nXValid, nYValid;

readBlock( theBandNo, iXBlock, iYBlock, myData );

// Compute the portion of the block that is valid
// for partial edge blocks.
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
nXValid = myBandXSize - iXBlock * myXBlockSize;
else
nXValid = myXBlockSize;

if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
nYValid = myBandYSize - iYBlock * myYBlockSize;
else
nYValid = myYBlockSize;

// Collect the histogram counts.
for ( int iY = 0; iY < nYValid; iY++ )
{
for ( int iX = 0; iX < nXValid; iX++ )
{
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
//QgsDebugMsg ( "myValue = " + QString::number(myValue) );

if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
{
continue; // NULL
}

myRasterBandStats.sumOfSquares += static_cast < double >
( pow( myValue - myRasterBandStats.mean, 2 ) );
}
}
} //end of column wise loop
} //end of row wise loop

//divide result by sample size - 1 and get square root to get stdev
myRasterBandStats.stdDev = static_cast < double >( sqrt( myRasterBandStats.sumOfSquares /
( myRasterBandStats.elementCount - 1 ) ) );

#ifdef QGISDEBUG
QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "NULL", noDataValue() , 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ );
#endif

CPLFree( myData );
myRasterBandStats.statsGathered = true;
return myRasterBandStats;
}

double QgsRasterDataProvider::readValue( void *data, int type, int index )
{
if ( !data )
return mValidNoDataValue ? noDataValue() : 0.0;

switch ( type )
{
case Byte:
return ( double )(( GByte * )data )[index];
break;
case UInt16:
return ( double )(( GUInt16 * )data )[index];
break;
case Int16:
return ( double )(( GInt16 * )data )[index];
break;
case UInt32:
return ( double )(( GUInt32 * )data )[index];
break;
case Int32:
return ( double )(( GInt32 * )data )[index];
break;
case Float32:
return ( double )(( float * )data )[index];
break;
case Float64:
return ( double )(( double * )data )[index];
break;
default:
QgsLogger::warning( "GDAL data type is not supported" );
}

return mValidNoDataValue ? noDataValue() : 0.0;
}

// ENDS
16 changes: 12 additions & 4 deletions src/core/qgsrasterdataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsrasterbandstats.h"

#include "cpl_conv.h"
#include <cmath>

class QImage;
Expand Down Expand Up @@ -109,10 +110,11 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider
};

// Progress types
enum Progress
enum RasterProgressType
{
ProgressHistogram = 0,
ProgressPyramids = 1
ProgressPyramids = 1,
ProgressStatistics = 2
};

QgsRasterDataProvider();
Expand Down Expand Up @@ -325,6 +327,9 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider

/** read block of data using give extent and size */
virtual void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS, void *data );

/* Read a value from a data block at a given index. */
virtual double readValue( void *data, int type, int index );

/** value representing null data */
virtual double noDataValue() const { return 0; }
Expand Down Expand Up @@ -365,13 +370,16 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider
*/
virtual QList<QgsRasterPyramid> buildPyramidList() { return QList<QgsRasterPyramid>(); };

/** If the provider supports it, return band stats for the
given band. Default behaviour is to blockwise read the data
and generate the stats unless the provider overloads this function. */
virtual QgsRasterBandStats bandStatistics( int theBandNo );

/** \brief helper function to create zero padded band names */
QString generateBandName( int theBandNumber )
{
return tr( "Band" ) + QString( " %1" ) .arg( theBandNumber, 1 + ( int ) log10(( float ) bandCount() ), 10, QChar( '0' ) );
}

};

/**
* Get metadata in a format suitable for feeding directly
Expand Down
2 changes: 1 addition & 1 deletion src/core/raster/qgspseudocolorshader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ bool QgsPseudoColorShader::shade( double theValue, int* theReturnRedValue, int*
}

//check if we are in the first class break
if (( myPixelValue >= mClassBreakMin1 ) && ( myPixelValue < mClassBreakMax1 ) )
if ( ( myPixelValue >= mClassBreakMin1 ) && ( myPixelValue < mClassBreakMax1 ) )
{
*theReturnRedValue = 0;
*theReturnGreenValue = static_cast < int >((( 255 / mMinimumMaximumRange ) * ( myPixelValue - mClassBreakMin1 ) ) * 3 );
Expand Down
165 changes: 6 additions & 159 deletions src/core/raster/qgsrasterlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo )
QgsRasterBandStats myNullReturnStats;
return myNullReturnStats;
}

// check if we have received a valid band number
if (( mDataProvider->bandCount() < theBandNo ) && mRasterType != Palette )
{
Expand All @@ -341,7 +340,6 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo )
QgsRasterBandStats myNullReturnStats;
return myNullReturnStats;
}

// check if we have previously gathered stats for this band...
if ( theBandNo < 1 || theBandNo > mRasterStatsList.size() )
{
Expand All @@ -358,174 +356,23 @@ const QgsRasterBandStats QgsRasterLayer::bandStatistics( int theBandNo )
{
return myRasterBandStats;
}
// only print message if we are actually gathering the stats
emit statusChanged( tr( "Retrieving stats for %1" ).arg( name() ) );
qApp->processEvents();
QgsDebugMsg( "stats for band " + QString::number( theBandNo ) );

myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later

emit statusChanged( tr( "Calculating stats for %1" ).arg( name() ) );
//reset the main app progress bar
emit drawingProgress( 0, 0 );

int myDataType = mDataProvider->dataType( theBandNo );

int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
myXBlockSize = mDataProvider->xBlockSize();
myYBlockSize = mDataProvider->yBlockSize();

myNXBlocks = ( mDataProvider->xSize() + myXBlockSize - 1 ) / myXBlockSize;
myNYBlocks = ( mDataProvider->ySize() + myYBlockSize - 1 ) / myYBlockSize;

void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( mDataProvider->dataTypeSize( theBandNo ) / 8 ) );

// unfortunately we need to make two passes through the data to calculate stddev
bool myFirstIterationFlag = true;

int myBandXSize = mDataProvider->xSize();
int myBandYSize = mDataProvider->ySize();
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
{
emit drawingProgress( iYBlock, myNYBlocks * 2 );

for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
{
int nXValid, nYValid;
mDataProvider->readBlock( theBandNo, iXBlock, iYBlock, myData );

// Compute the portion of the block that is valid
// for partial edge blocks.
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
nXValid = myBandXSize - iXBlock * myXBlockSize;
else
nXValid = myXBlockSize;

if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
nYValid = myBandYSize - iYBlock * myYBlockSize;
else
nYValid = myYBlockSize;

// Collect the histogram counts.
for ( int iY = 0; iY < nYValid; iY++ )
{
for ( int iX = 0; iX < nXValid; iX++ )
{
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) );

if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) )
{
continue; // NULL
}

myRasterBandStats.sum += myValue;
++myRasterBandStats.elementCount;
//only use this element if we have a non null element
if ( myFirstIterationFlag )
{
//this is the first iteration so initialise vars
myFirstIterationFlag = false;
myRasterBandStats.minimumValue = myValue;
myRasterBandStats.maximumValue = myValue;
} //end of true part for first iteration check
else
{
//this is done for all subsequent iterations
if ( myValue < myRasterBandStats.minimumValue )
{
myRasterBandStats.minimumValue = myValue;
}
if ( myValue > myRasterBandStats.maximumValue )
{
myRasterBandStats.maximumValue = myValue;
}
} //end of false part for first iteration check
}
}
} //end of column wise loop
} //end of row wise loop


//end of first pass through data now calculate the range
myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
//calculate the mean
myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;

//for the second pass we will get the sum of the squares / mean
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
{
emit drawingProgress( iYBlock + myNYBlocks, myNYBlocks * 2 );

for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
{
int nXValid, nYValid;

mDataProvider->readBlock( theBandNo, iXBlock, iYBlock, myData );

// Compute the portion of the block that is valid
// for partial edge blocks.
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
nXValid = myBandXSize - iXBlock * myXBlockSize;
else
nXValid = myXBlockSize;

if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
nYValid = myBandYSize - iYBlock * myYBlockSize;
else
nYValid = myYBlockSize;

// Collect the histogram counts.
for ( int iY = 0; iY < nYValid; iY++ )
{
for ( int iX = 0; iX < nXValid; iX++ )
{
double myValue = readValue( myData, myDataType, iX + ( iY * myXBlockSize ) );
//QgsDebugMsg ( "myValue = " + QString::number(myValue) );

if ( mValidNoDataValue && ( qAbs( myValue - mNoDataValue ) <= TINY_VALUE || myValue != myValue ) )
{
continue; // NULL
}

myRasterBandStats.sumOfSquares += static_cast < double >
( pow( myValue - myRasterBandStats.mean, 2 ) );
}
}
} //end of column wise loop
} //end of row wise loop

//divide result by sample size - 1 and get square root to get stdev
myRasterBandStats.stdDev = static_cast < double >( sqrt( myRasterBandStats.sumOfSquares /
( myRasterBandStats.elementCount - 1 ) ) );

#ifdef QGISDEBUG
QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "NULL", mNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ );
#endif

CPLFree( myData );
myRasterBandStats.statsGathered = true;

myRasterBandStats = mDataProvider->bandStatistics( theBandNo );
QgsDebugMsg( "adding stats to stats collection at position " + QString::number( theBandNo - 1 ) );
//add this band to the class stats map
mRasterStatsList[theBandNo - 1] = myRasterBandStats;
emit drawingProgress( mHeight, mHeight ); //reset progress
//QApplication::restoreOverrideCursor(); //restore the cursor
QgsDebugMsg( "Stats collection completed returning" );
return myRasterBandStats;

} // QgsRasterLayer::bandStatistics

const QgsRasterBandStats QgsRasterLayer::bandStatistics( QString const & theBandName )
{

// only print message if we are actually gathering the stats
emit statusChanged( tr( "Retrieving stats for %1" ).arg( name() ) );
qApp->processEvents();
//reset the main app progress bar
emit drawingProgress( 0, 0 );
//we cant use a vector iterator because the iterator is astruct not a class
//and the qvector model does not like this.
for ( int i = 1; i <= mDataProvider->bandCount(); i++ )
Expand Down
147 changes: 94 additions & 53 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri )

// To get buildSupportedRasterFileFilter the provider is called with empty uri
if ( uri.isEmpty() )
{
return;
}

mGdalDataset = NULL;

Expand Down Expand Up @@ -287,51 +289,6 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri )
QgsDebugMsg( QString( "mNoDataValue[%1] = %1" ).arg( i - 1 ).arg( mNoDataValue[i-1] ) );
}

// This block of code was in old version in QgsRasterLayer::bandStatistics
//ifdefs below to remove compiler warning about unused vars
#ifdef QGISDEBUG
#if 0
int success;
double GDALminimum = GDALGetRasterMinimum( myGdalBand, &success );

if ( ! success )
{
QgsDebugMsg( "myGdalBand->GetMinimum() failed" );
}

double GDALmaximum = GDALGetRasterMaximum( myGdalBand, &success );

if ( ! success )
{
QgsDebugMsg( "myGdalBand->GetMaximum() failed" );
}

double GDALnodata = GDALGetRasterNoDataValue( myGdalBand, &success );

if ( ! success )
{
QgsDebugMsg( "myGdalBand->GetNoDataValue() failed" );
}

QgsLogger::debug( "GDALminium: ", GDALminimum, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "GDALmaximum: ", GDALmaximum, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "GDALnodata: ", GDALnodata, __FILE__, __FUNCTION__, __LINE__ );

double GDALrange[2]; // calculated min/max, as opposed to the
// dataset provided

GDALComputeRasterMinMax( myGdalBand, 1, GDALrange );
QgsLogger::debug( "approximate computed GDALminium:", GDALrange[0], __FILE__, __FUNCTION__, __LINE__, 1 );
QgsLogger::debug( "approximate computed GDALmaximum:", GDALrange[1], __FILE__, __FUNCTION__, __LINE__, 1 );

GDALComputeRasterMinMax( myGdalBand, 0, GDALrange );
QgsLogger::debug( "exactly computed GDALminium:", GDALrange[0] );
QgsLogger::debug( "exactly computed GDALmaximum:", GDALrange[1] );

QgsDebugMsg( "starting manual stat computation" );
#endif
#endif

mValid = true;
QgsDebugMsg( "end" );
}
Expand Down Expand Up @@ -391,7 +348,9 @@ QgsGdalProvider::~QgsGdalProvider()
void QgsGdalProvider::closeDataset()
{
if ( !mValid )
{
return;
}
mValid = false;

GDALDereferenceDataset( mGdalBaseDataset );
Expand Down Expand Up @@ -921,25 +880,37 @@ void QgsGdalProvider::computeMinMax( int theBandNo )
{
QgsDebugMsg( QString( "theBandNo = %1 mMinMaxComputed = %2" ).arg( theBandNo ).arg( mMinMaxComputed[theBandNo-1] ) );
if ( mMinMaxComputed[theBandNo-1] )
{
return;
double GDALrange[2];
}
int bApproxOK=false;
double pdfMin;
double pdfMax;
double pdfMean;
double pdfStdDev;
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
GDALComputeRasterMinMax( myGdalBand, 1, GDALrange ); //Approximate
QgsDebugMsg( QString( "GDALrange[0] = %1 GDALrange[1] = %2" ).arg( GDALrange[0] ).arg( GDALrange[1] ) );
mMinimum[theBandNo-1] = GDALrange[0];
mMaximum[theBandNo-1] = GDALrange[1];
double myerval = GDALGetRasterStatistics (
myGdalBand,
bApproxOK,
TRUE,
&pdfMin,
&pdfMax,
&pdfMean,
&pdfStdDev
);
Q_UNUSED(myerval);
mMinimum[theBandNo-1] = pdfMin;
mMaximum[theBandNo-1] = pdfMax;
}

double QgsGdalProvider::minimumValue( int theBandNo ) const
{
QgsDebugMsg( QString( "theBandNo = %1" ).arg( theBandNo ) );
//computeMinMax ( theBandNo );
return mMinimum[theBandNo-1];
}
double QgsGdalProvider::maximumValue( int theBandNo ) const
{
QgsDebugMsg( QString( "theBandNo = %1" ).arg( theBandNo ) );
//computeMinMax ( theBandNo );
return mMaximum[theBandNo-1];
}

Expand Down Expand Up @@ -1294,7 +1265,9 @@ void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & t
//vector is not equal to the number of bins
//i.e if the histogram has never previously been generated or the user has
//selected a new number of bins.
if ( theBandStats.histogramVector->size() != theBinCount ||
bool myCollectHistogramFlag = true;
if ( theBandStats.histogramVector == 0 ||
theBandStats.histogramVector->size() != theBinCount ||
theIgnoreOutOfRangeFlag != theBandStats.isHistogramOutOfRange ||
theHistogramEstimatedFlag != theBandStats.isHistogramEstimated )
{
Expand Down Expand Up @@ -1865,6 +1838,74 @@ QGISEXTERN bool isValidRasterFileName( QString const & theFileNameQString, QStri
}
}



QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo )
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
QgsRasterBandStats myRasterBandStats;
int bApproxOK=false;
double pdfMin;
double pdfMax;
double pdfMean;
double pdfStdDev;
QgsGdalProgress myProg;
myProg.type = ProgressHistogram;
myProg.provider = this;

// Suggested by Etienne Sky to use getrasterstatistics instead of compute
// since computerasterstatistics forces collection each time
// where as getrasterstatistics uses aux.xml cached copy if available
// Note: there is currently no progress callback in this method
double myerval = GDALGetRasterStatistics (
myGdalBand,
bApproxOK,
TRUE,
&pdfMin,
&pdfMax,
&pdfMean,
&pdfStdDev
);
//double myerval = GDALComputeRasterStatistics ( myGdalBand,
// bApproxOK,
// &pdfMin,
// &pdfMax,
// &pdfMean,
// &pdfStdDev,
// progressCallback,
// &myProg
//) ;
//end of first pass through data now calculate the range
myRasterBandStats.bandName = generateBandName( theBandNo );
myRasterBandStats.bandNumber = theBandNo;
myRasterBandStats.range = pdfMax - pdfMin;
myRasterBandStats.minimumValue = pdfMin;
myRasterBandStats.maximumValue = pdfMax;
//calculate the mean
myRasterBandStats.mean = pdfMean;
myRasterBandStats.sum = 0; //not available via gdal
myRasterBandStats.elementCount = mWidth * mHeight;
myRasterBandStats.sumOfSquares = 0; //not available via gdal
myRasterBandStats.stdDev = pdfStdDev;
myRasterBandStats.statsGathered = true;

#ifdef QGISDEBUG
QgsLogger::debug( "************ STATS **************", 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "VALID NODATA", mValidNoDataValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MIN", myRasterBandStats.minimumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MAX", myRasterBandStats.maximumValue, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "RANGE", myRasterBandStats.range, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "MEAN", myRasterBandStats.mean, 1, __FILE__, __FUNCTION__, __LINE__ );
QgsLogger::debug( "STDDEV", myRasterBandStats.stdDev, 1, __FILE__, __FUNCTION__, __LINE__ );
#endif

myRasterBandStats.statsGathered = true;

return myRasterBandStats;

} // QgsGdalProvider::bandStatistics


/**
Builds the list of file filter strings to later be used by
QgisApp::addRasterLayer()
Expand Down
8 changes: 8 additions & 0 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "qgsrasterdataprovider.h"
#include "qgsrectangle.h"
#include "qgscolorrampshader.h"
#include "qgsrasterbandstats.h"

#include <QString>
#include <QStringList>
Expand Down Expand Up @@ -222,6 +223,13 @@ class QgsGdalProvider : public QgsRasterDataProvider

/** \brief Returns the sublayers of this layer - Useful for providers that manage their own layers, such as WMS */
QStringList subLayers() const;
/** \brief If the provider supports it, return band stats for the
given band.
@note added in QGIS 1.7
@note overloads virtual method from QgsRasterProvider::bandStatistics
*/
QgsRasterBandStats bandStatistics( int theBandNo );

void populateHistogram( int theBandNoInt,
QgsRasterBandStats & theBandStats,
Expand Down
2 changes: 1 addition & 1 deletion tests/src/core/testqgsapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void TestQgsApplication::initTestCase()
// init QGIS's paths - true means that all path will be inited from prefix
QString qgisPath = QCoreApplication::applicationDirPath();
QgsApplication::setPrefixPath( INSTALL_PREFIX, true );
QgsApplication::showSettings();
qDebug( QgsApplication::showSettings().toUtf8() );
};

void TestQgsApplication::checkTheme()
Expand Down
28 changes: 24 additions & 4 deletions tests/src/core/testqgsrasterlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class TestQgsRasterLayer: public QObject
void landsatBasic();
void landsatBasic875Qml();
void checkDimensions();
void checkStats();
void buildExternalOverviews();
void registry();
private:
Expand All @@ -73,9 +74,10 @@ class TestQgsRasterLayer: public QObject
void TestQgsRasterLayer::initTestCase()
{
// init QGIS's paths - true means that all path will be inited from prefix
QString qgisPath = QCoreApplication::applicationDirPath();
QgsApplication::setPrefixPath( INSTALL_PREFIX, true );
QgsApplication::showSettings();
QgsApplication::init( QString() );
QgsApplication::initQgis();
QString mySettings = QgsApplication::showSettings();
mySettings = mySettings.replace("\n","<br />");
//create some objects that will be used in all tests...
//create a raster layer that will be used in all tests...
mTestDataDir = QString( TEST_DATA_DIR ) + QDir::separator(); //defined in CmakeLists.txt
Expand All @@ -96,6 +98,7 @@ void TestQgsRasterLayer::initTestCase()
myLayers << mpRasterLayer->id();
mpMapRenderer->setLayerSet( myLayers );
mReport += "<h1>Raster Layer Tests</h1>\n";
mReport += "<p>" + mySettings + "</p>";
}
//runs after all tests
void TestQgsRasterLayer::cleanupTestCase()
Expand All @@ -115,6 +118,7 @@ void TestQgsRasterLayer::cleanupTestCase()
void TestQgsRasterLayer::isValid()
{
QVERIFY( mpRasterLayer->isValid() );
mpRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, false );
mpMapRenderer->setExtent( mpRasterLayer->extent() );
QVERIFY( render( "raster" ) );
}
Expand All @@ -133,6 +137,7 @@ void TestQgsRasterLayer::pseudoColor()

void TestQgsRasterLayer::landsatBasic()
{
mpLandsatRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, false );
QStringList myLayers;
myLayers << mpLandsatRasterLayer->id();
mpMapRenderer->setLayerSet( myLayers );
Expand All @@ -156,13 +161,26 @@ void TestQgsRasterLayer::checkDimensions()
// regression check for ticket #832
// note bandStatistics call is base 1
QVERIFY( mpRasterLayer->bandStatistics( 1 ).elementCount == 100 );
mReport += "<h2>Check Dimensions</h2>\n";
mReport += "<p>Passed</p>";
}
void TestQgsRasterLayer::checkStats()
{
QVERIFY( mpRasterLayer->width() == 10 );
QVERIFY( mpRasterLayer->height() == 10 );
QVERIFY( mpRasterLayer->bandStatistics( 1 ).elementCount == 100 );
QVERIFY( mpRasterLayer->bandStatistics( 1 ).minimumValue == 0 );
QVERIFY( mpRasterLayer->bandStatistics( 1 ).maximumValue == 9 );
QVERIFY( mpRasterLayer->bandStatistics( 1 ).mean == 4.5 );
QVERIFY( mpRasterLayer->bandStatistics( 1 ).stdDev == 2.872281323269 );
mReport += "<h2>Check Stats</h2>\n";
mReport += "<p>Passed</p>";
}

void TestQgsRasterLayer::buildExternalOverviews()
{
//before we begin delete any old ovr file (if it exists)
//and make a copy of the landsat raster into the temp dir

QString myTempPath = QDir::tempPath() + QDir::separator();
QFile::remove( myTempPath + "landsat.tif.ovr" );
QFile::copy( mTestDataDir + "landsat.tif", myTempPath + "landsat.tif" );
Expand Down Expand Up @@ -205,6 +223,8 @@ void TestQgsRasterLayer::buildExternalOverviews()
QVERIFY( QFile::exists( myTempPath + "landsat.tif.ovr" ) );
//cleanup
delete mypLayer;
mReport += "<h2>Check Overviews</h2>\n";
mReport += "<p>Passed</p>";
}


Expand Down
Binary file modified tests/testdata/expected_landsat_875.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/testdata/expected_landsat_basic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 62 additions & 71 deletions tests/testdata/landsat_875.qml
Original file line number Diff line number Diff line change
@@ -1,73 +1,64 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis projectname="" version="0.9.2-Ganymede" >
<maplayer minimumScale="1" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0" type="raster" >
<id>landsat_clip20080125220410500</id>
<datasource>landsat_clip.tif</datasource>
<layername>landsat_clip</layername>
<srs>
<spatialrefsys>
<proj4>+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs</proj4>
<srsid>2267</srsid>
<srid>32633</srid>
<epsg>32633</epsg>
<description>WGS 84 / UTM zone 33N</description>
<projectionacronym>utm</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</srs>
<transparencyLevelInt>255</transparencyLevelInt>
<provider></provider>
<rasterproperties>
<mDebugOverlayFlag boolean="false" />
<drawingStyle>MULTI_BAND_COLOR</drawingStyle>
<mColorShadingAlgorithm>UNDEFINED_SHADING_ALGORITHM</mColorShadingAlgorithm>
<mInvertPixelsFlag boolean="false" />
<mRedBandName>8 : Undefined</mRedBandName>
<mGreenBandName>7 : Undefined</mGreenBandName>
<mBlueBandName>5 : Undefined</mBlueBandName>
<mGrayBandName>Not Set</mGrayBandName>
<mStandardDeviations>0</mStandardDeviations>
<mContrastEnhancementAlgorithm>STRETCH_TO_MINMAX</mContrastEnhancementAlgorithm>
<contrastEnhancementMinMaxValues>
<minMaxEntry>
<min>122</min>
<max>130</max>
</minMaxEntry>
<minMaxEntry>
<min>133</min>
<max>148</max>
</minMaxEntry>
<minMaxEntry>
<min>57</min>
<max>157</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>61</min>
<max>117</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>92</min>
<max>213</max>
</minMaxEntry>
<minMaxEntry>
<min>122</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
</contrastEnhancementMinMaxValues>
<mNoDataValue mValidNoDataValue="false" >-9999.000000</mNoDataValue>
</rasterproperties>
</maplayer>
<qgis version="1.8.0-Trunk" minimumScale="0" maximumScale="1e+08" hasScaleBasedVisibilityFlag="0">
<transparencyLevelInt>255</transparencyLevelInt>
<rasterproperties>
<mDrawingStyle>MultiBandColor</mDrawingStyle>
<mColorShadingAlgorithm>UndefinedShader</mColorShadingAlgorithm>
<mInvertColor boolean="false"/>
<mRedBandName>Band 8</mRedBandName>
<mGreenBandName>Band 7</mGreenBandName>
<mBlueBandName>Band 5</mBlueBandName>
<mGrayBandName>Band 1</mGrayBandName>
<mStandardDeviations>0</mStandardDeviations>
<mUserDefinedRGBMinimumMaximum boolean="false"/>
<mRGBMinimumMaximumEstimated boolean="true"/>
<mUserDefinedGrayMinimumMaximum boolean="false"/>
<mGrayMinimumMaximumEstimated boolean="true"/>
<mContrastEnhancementAlgorithm>StretchToMinimumMaximum</mContrastEnhancementAlgorithm>
<contrastEnhancementMinMaxValues>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>61</min>
<max>123</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>92</min>
<max>232</max>
</minMaxEntry>
<minMaxEntry>
<min>122</min>
<max>255</max>
</minMaxEntry>
<minMaxEntry>
<min>0</min>
<max>255</max>
</minMaxEntry>
</contrastEnhancementMinMaxValues>
<mNoDataValue mValidNoDataValue="true">-32768.000000</mNoDataValue>
<singleValuePixelList>
<pixelListEntry pixelValue="-32768.000000" percentTransparent="100"/>
</singleValuePixelList>
<threeValuePixelList>
<pixelListEntry red="-32768.000000" blue="-32768.000000" green="-32768.000000" percentTransparent="100"/>
</threeValuePixelList>
</rasterproperties>
</qgis>