mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
raster histograms separated from stats, generic implementation (used by GRASS,WCS), more histogram and stats options (extent, sample size), stats and histograms cached in providers
This commit is contained in:
parent
d794522893
commit
9980b454c0
@ -35,13 +35,6 @@ class QgsRasterBandStats
|
||||
/** \brief The number of cells in the band. Equivalent to height x width.
|
||||
* TODO: check if NO_DATA are excluded!*/
|
||||
int elementCount;
|
||||
/** \brief Store the histogram for a given layer */
|
||||
typedef QVector<int> HistogramVector;
|
||||
//HistogramVector * histogramVector;
|
||||
/** \brief whteher histogram values are estimated or completely calculated */
|
||||
bool isHistogramEstimated;
|
||||
/** whehter histogram compuation should include out of range values */
|
||||
bool isHistogramOutOfRange;
|
||||
/** Color table */
|
||||
//QList<QgsColorRampShader::ColorRampItem> colorTable;
|
||||
};
|
||||
|
@ -271,9 +271,10 @@ QByteArray QgsRasterDataProvider::noValueBytes( int theBandNo )
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
/*
|
||||
QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
||||
{
|
||||
// TODO: cache stats here in provider
|
||||
double myNoDataValue = noDataValue();
|
||||
QgsRasterBandStats myRasterBandStats;
|
||||
myRasterBandStats.elementCount = 0; // because we'll be counting only VALID pixels later
|
||||
@ -282,19 +283,15 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
||||
|
||||
int myDataType = dataType( theBandNo );
|
||||
|
||||
int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
|
||||
myXBlockSize = xBlockSize();
|
||||
myYBlockSize = yBlockSize();
|
||||
|
||||
if ( myXBlockSize == 0 || myYBlockSize == 0 )
|
||||
if ( xBlockSize() == 0 || yBlockSize() == 0 )
|
||||
{
|
||||
return QgsRasterBandStats(); //invalid raster band stats
|
||||
}
|
||||
|
||||
myNXBlocks = ( xSize() + myXBlockSize - 1 ) / myXBlockSize;
|
||||
myNYBlocks = ( ySize() + myYBlockSize - 1 ) / myYBlockSize;
|
||||
int myNXBlocks = ( xSize() + xBlockSize() - 1 ) / xBlockSize();
|
||||
int myNYBlocks = ( ySize() + yBlockSize() - 1 ) / yBlockSize();
|
||||
|
||||
void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) );
|
||||
void *myData = CPLMalloc( xBlockSize() * yBlockSize() * ( dataTypeSize( theBandNo ) / 8 ) );
|
||||
|
||||
// unfortunately we need to make two passes through the data to calculate stddev
|
||||
bool myFirstIterationFlag = true;
|
||||
@ -310,22 +307,22 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
||||
|
||||
// Compute the portion of the block that is valid
|
||||
// for partial edge blocks.
|
||||
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
|
||||
nXValid = myBandXSize - iXBlock * myXBlockSize;
|
||||
if (( iXBlock + 1 ) * xBlockSize() > myBandXSize )
|
||||
nXValid = myBandXSize - iXBlock * xBlockSize();
|
||||
else
|
||||
nXValid = myXBlockSize;
|
||||
nXValid = xBlockSize();
|
||||
|
||||
if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
|
||||
nYValid = myBandYSize - iYBlock * myYBlockSize;
|
||||
if (( iYBlock + 1 ) * yBlockSize() > myBandYSize )
|
||||
nYValid = myBandYSize - iYBlock * yBlockSize();
|
||||
else
|
||||
nYValid = myYBlockSize;
|
||||
nYValid = yBlockSize();
|
||||
|
||||
// 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 ) );
|
||||
double myValue = readValue( myData, myDataType, iX + ( iY * xBlockSize() ) );
|
||||
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) );
|
||||
|
||||
if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
|
||||
@ -377,22 +374,22 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
||||
|
||||
// Compute the portion of the block that is valid
|
||||
// for partial edge blocks.
|
||||
if (( iXBlock + 1 ) * myXBlockSize > myBandXSize )
|
||||
nXValid = myBandXSize - iXBlock * myXBlockSize;
|
||||
if (( iXBlock + 1 ) * xBlockSize() > myBandXSize )
|
||||
nXValid = myBandXSize - iXBlock * xBlockSize();
|
||||
else
|
||||
nXValid = myXBlockSize;
|
||||
nXValid = xBlockSize();
|
||||
|
||||
if (( iYBlock + 1 ) * myYBlockSize > myBandYSize )
|
||||
nYValid = myBandYSize - iYBlock * myYBlockSize;
|
||||
if (( iYBlock + 1 ) * yBlockSize() > myBandYSize )
|
||||
nYValid = myBandYSize - iYBlock * yBlockSize();
|
||||
else
|
||||
nYValid = myYBlockSize;
|
||||
nYValid = yBlockSize();
|
||||
|
||||
// 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 ) );
|
||||
double myValue = readValue( myData, myDataType, iX + ( iY * xBlockSize() ) );
|
||||
//QgsDebugMsg ( "myValue = " + QString::number(myValue) );
|
||||
|
||||
if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
|
||||
@ -411,41 +408,32 @@ QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo )
|
||||
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
|
||||
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 ) );
|
||||
QgsDebugMsg( QString( "MEAN %1" ).arg( myRasterBandStats.mean ) );
|
||||
QgsDebugMsg( QString( "STDDEV %1" ).arg( myRasterBandStats.stdDev ) );
|
||||
|
||||
CPLFree( myData );
|
||||
myRasterBandStats.statsGathered = true;
|
||||
return myRasterBandStats;
|
||||
}
|
||||
|
||||
QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo,
|
||||
double theMinimum, double theMaximum,
|
||||
int theBinCount,
|
||||
*/
|
||||
QgsRasterBandStats QgsRasterDataProvider::statisticsDefaults( int theBandNo,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
int theSampleSize )
|
||||
{
|
||||
QgsRasterHistogram myHistogram;
|
||||
myHistogram.bandNumber = theBandNo;
|
||||
myHistogram.minimum = theMinimum;
|
||||
myHistogram.maximum = theMaximum;
|
||||
myHistogram.includeOutOfRange = theIncludeOutOfRange;
|
||||
//myHistogram.sampleSize = theSampleSize
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theSampleSize = %2" ).arg( theBandNo ).arg( theSampleSize ) );
|
||||
|
||||
QgsRasterBandStats myRasterBandStats;
|
||||
myRasterBandStats.bandName = generateBandName( theBandNo );
|
||||
myRasterBandStats.bandNumber = theBandNo;
|
||||
|
||||
// First calc defaults
|
||||
QgsRectangle myExtent = theExtent.isEmpty() ? extent() : theExtent;
|
||||
myHistogram.extent = myExtent;
|
||||
myRasterBandStats.extent = myExtent;
|
||||
|
||||
int myWidth, myHeight;
|
||||
if ( theSampleSize > 0 )
|
||||
{
|
||||
// Calc resolution from theSampleSize
|
||||
@ -460,114 +448,374 @@ QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo,
|
||||
if ( xRes < srcXRes ) xRes = srcXRes;
|
||||
if ( yRes < srcYRes ) yRes = srcYRes;
|
||||
}
|
||||
QgsDebugMsg( QString( "xRes = %1 yRes = %2" ).arg( xRes ).arg( yRes ) );
|
||||
|
||||
myWidth = static_cast <int>( myExtent.width() / xRes );
|
||||
myHeight = static_cast <int>( myExtent.height() / yRes );
|
||||
myRasterBandStats.width = static_cast <int>( myExtent.width() / xRes );
|
||||
myRasterBandStats.height = static_cast <int>( myExtent.height() / yRes );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( capabilities() & Size )
|
||||
{
|
||||
myWidth = xSize();
|
||||
myHeight = ySize();
|
||||
myRasterBandStats.width = xSize();
|
||||
myRasterBandStats.height = ySize();
|
||||
}
|
||||
else
|
||||
{
|
||||
myWidth = 1000;
|
||||
myHeight = 1000;
|
||||
myRasterBandStats.width = 1000;
|
||||
myRasterBandStats.height = 1000;
|
||||
}
|
||||
}
|
||||
myHistogram.width = myWidth;
|
||||
myHistogram.height = myHeight;
|
||||
QgsDebugMsg( QString( "myWidth = %1 myHeight = %2" ).arg( myWidth ).arg( myHeight ) );
|
||||
QgsDebugMsg( QString( "myRasterBandStats.width = %1 myRasterBandStats.height = %2" ).arg( myRasterBandStats.width ).arg( myRasterBandStats.height ) );
|
||||
|
||||
return myRasterBandStats;
|
||||
}
|
||||
|
||||
// Find cached
|
||||
QgsRasterBandStats QgsRasterDataProvider::bandStatistics( int theBandNo,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize )
|
||||
{
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theSampleSize = %2" ).arg( theBandNo ).arg( theSampleSize ) );
|
||||
QgsRasterBandStats myRasterBandStats = statisticsDefaults( theBandNo, theExtent, theSampleSize );
|
||||
|
||||
foreach( QgsRasterBandStats stats, mStatistics )
|
||||
{
|
||||
if ( stats == myRasterBandStats )
|
||||
{
|
||||
QgsDebugMsg( "Using cached statistics." );
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
QgsRectangle myExtent = myRasterBandStats.extent;
|
||||
int myWidth = myRasterBandStats.width;
|
||||
int myHeight = myRasterBandStats.height;
|
||||
|
||||
double myNoDataValue = noDataValue();
|
||||
int myDataType = dataType( theBandNo );
|
||||
|
||||
int myBinCount = theBinCount;
|
||||
if ( myBinCount == 0 )
|
||||
if ( xBlockSize() == 0 || yBlockSize() == 0 ) // should not happen
|
||||
{
|
||||
if ( myDataType == QgsRasterDataProvider::Byte )
|
||||
{
|
||||
myBinCount = 256; // Cannot store more values in byte
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is no best default value, to display something reasonable in histogram chart, binCount should be small, OTOH, to get precise data for cumulative cut, the number should be big. Because it is easier to define fixed lower value for the chart, we calc optimum binCount for higher resolution (to avoid calculating that where histogram() is used. In any any case, it does not make sense to use more than width*height;
|
||||
myBinCount = myWidth * myHeight;
|
||||
if ( myBinCount > 1000 ) myBinCount = 1000;
|
||||
}
|
||||
}
|
||||
myHistogram.binCount = theBinCount;
|
||||
QgsDebugMsg( QString( "myBinCount = %1" ).arg( myBinCount ) );
|
||||
|
||||
// Check if we have cached
|
||||
foreach( QgsRasterHistogram histogram, mHistograms )
|
||||
{
|
||||
if ( histogram.bandNumber == theBandNo &&
|
||||
histogram.minimum == theMinimum &&
|
||||
histogram.maximum == theMaximum &&
|
||||
histogram.binCount == myBinCount &&
|
||||
histogram.extent == myExtent &&
|
||||
histogram.width == myWidth &&
|
||||
histogram.height == myHeight &&
|
||||
histogram.includeOutOfRange == theIncludeOutOfRange )
|
||||
{
|
||||
return histogram;
|
||||
}
|
||||
return myRasterBandStats;
|
||||
}
|
||||
|
||||
myHistogram.histogramVector.resize( myBinCount );
|
||||
int myNXBlocks = ( myWidth + xBlockSize() - 1 ) / xBlockSize();
|
||||
int myNYBlocks = ( myHeight + yBlockSize() - 1 ) / yBlockSize();
|
||||
|
||||
int myNXBlocks, myNYBlocks, myXBlockSize, myYBlockSize;
|
||||
myXBlockSize = xBlockSize();
|
||||
myYBlockSize = yBlockSize();
|
||||
void *myData = CPLMalloc( xBlockSize() * yBlockSize() * ( dataTypeSize( theBandNo ) / 8 ) );
|
||||
|
||||
if ( myXBlockSize == 0 || myYBlockSize == 0 ) // should not happen
|
||||
{
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
myNXBlocks = ( myWidth + myXBlockSize - 1 ) / myXBlockSize;
|
||||
myNYBlocks = ( myHeight + myYBlockSize - 1 ) / myYBlockSize;
|
||||
|
||||
void *myData = CPLMalloc( myXBlockSize * myYBlockSize * ( dataTypeSize( theBandNo ) / 8 ) );
|
||||
|
||||
int myBandXSize = xSize();
|
||||
int myBandYSize = ySize();
|
||||
double myXRes = myExtent.width() / myWidth;
|
||||
double myYRes = myExtent.height() / myHeight;
|
||||
double binSize = ( theMaximum - theMinimum ) / myBinCount;
|
||||
for ( int iYBlock = 0; iYBlock < myNYBlocks; iYBlock++ )
|
||||
{
|
||||
for ( int iXBlock = 0; iXBlock < myNXBlocks; iXBlock++ )
|
||||
{
|
||||
int myPartWidth = qMin( myXBlockSize, myWidth - iXBlock * myXBlockSize );
|
||||
int myPartHeight = qMin( myYBlockSize, myHeight - iYBlock * myYBlockSize );
|
||||
// TODO: progress signals
|
||||
|
||||
double xmin = myExtent.xMinimum() + iXBlock * myXBlockSize * myXRes;
|
||||
double xmax = xmin + myPartWidth * myXRes;
|
||||
double ymin = myExtent.yMaximum() - iYBlock * myYBlockSize * myYRes;
|
||||
double ymax = ymin - myPartHeight * myYRes;
|
||||
// used by single pass stdev
|
||||
double myMean = 0;
|
||||
double mySumOfSquares = 0;
|
||||
|
||||
bool myFirstIterationFlag = true;
|
||||
for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ )
|
||||
{
|
||||
for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
|
||||
{
|
||||
int myBlockWidth = qMin( xBlockSize(), myWidth - myXBlock * xBlockSize() );
|
||||
int myBlockHeight = qMin( yBlockSize(), myHeight - myYBlock * yBlockSize() );
|
||||
|
||||
double xmin = myExtent.xMinimum() + myXBlock * xBlockSize() * myXRes;
|
||||
double xmax = xmin + myBlockWidth * myXRes;
|
||||
double ymin = myExtent.yMaximum() - myYBlock * yBlockSize() * myYRes;
|
||||
double ymax = ymin - myBlockHeight * myYRes;
|
||||
|
||||
QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );
|
||||
|
||||
readBlock( theBandNo, myPartExtent, myPartWidth, myPartHeight, myData );
|
||||
readBlock( theBandNo, myPartExtent, myBlockWidth, myBlockHeight, myData );
|
||||
|
||||
// Collect the histogram counts.
|
||||
for ( int iY = 0; iY < myPartHeight; iY++ )
|
||||
for ( int myY = 0; myY < myBlockHeight; myY++ )
|
||||
{
|
||||
for ( int iX = 0; iX < myPartWidth; iX++ )
|
||||
for ( int myX = 0; myX < myBlockWidth; myX++ )
|
||||
{
|
||||
double myValue = readValue( myData, myDataType, iX + ( iY * myPartWidth ) );
|
||||
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (iX).arg(iY).arg( myValue ) );
|
||||
double myValue = readValue( myData, myDataType, myX + ( myY * myBlockWidth ) );
|
||||
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (myX).arg(myY).arg( myValue ) );
|
||||
|
||||
if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
|
||||
{
|
||||
continue; // NULL
|
||||
}
|
||||
|
||||
int myBinIndex = static_cast <int>( floor(( myValue - theMinimum ) / binSize ) ) ;
|
||||
myRasterBandStats.sum += myValue;
|
||||
myRasterBandStats.elementCount++;
|
||||
|
||||
if ( myFirstIterationFlag )
|
||||
{
|
||||
myFirstIterationFlag = false;
|
||||
myRasterBandStats.minimumValue = myValue;
|
||||
myRasterBandStats.maximumValue = myValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( myValue < myRasterBandStats.minimumValue )
|
||||
{
|
||||
myRasterBandStats.minimumValue = myValue;
|
||||
}
|
||||
if ( myValue > myRasterBandStats.maximumValue )
|
||||
{
|
||||
myRasterBandStats.maximumValue = myValue;
|
||||
}
|
||||
}
|
||||
|
||||
//myRasterBandStats.sumOfSquares += static_cast < double >
|
||||
// ( pow( myValue - myRasterBandStats.mean, 2 ) );
|
||||
|
||||
// Single pass stdev
|
||||
double myDelta = myValue - myMean;
|
||||
myMean += myDelta / myRasterBandStats.elementCount;
|
||||
mySumOfSquares += myDelta * ( myValue - myMean );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myRasterBandStats.range = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
|
||||
myRasterBandStats.mean = myRasterBandStats.sum / myRasterBandStats.elementCount;
|
||||
|
||||
myRasterBandStats.sumOfSquares = mySumOfSquares; // OK with single pass?
|
||||
|
||||
// stdDev may differ from GDAL stats, because GDAL is using naive single pass
|
||||
// algorithm which is more error prone (because of rounding errors)
|
||||
// Divide result by sample size - 1 and get square root to get stdev
|
||||
myRasterBandStats.stdDev = sqrt( mySumOfSquares / ( myRasterBandStats.elementCount - 1 ) );
|
||||
|
||||
QgsDebugMsg( "************ STATS **************" );
|
||||
QgsDebugMsg( QString( "VALID NODATA %1" ).arg( mValidNoDataValue ) );
|
||||
QgsDebugMsg( QString( "MIN %1" ).arg( myRasterBandStats.minimumValue ) );
|
||||
QgsDebugMsg( QString( "MAX %1" ).arg( myRasterBandStats.maximumValue ) );
|
||||
QgsDebugMsg( QString( "RANGE %1" ).arg( myRasterBandStats.range ) );
|
||||
QgsDebugMsg( QString( "MEAN %1" ).arg( myRasterBandStats.mean ) );
|
||||
QgsDebugMsg( QString( "STDDEV %1" ).arg( myRasterBandStats.stdDev ) );
|
||||
|
||||
CPLFree( myData );
|
||||
|
||||
myRasterBandStats.statsGathered = true;
|
||||
mStatistics.append( myRasterBandStats );
|
||||
|
||||
return myRasterBandStats;
|
||||
}
|
||||
|
||||
QgsRasterHistogram QgsRasterDataProvider::histogramDefaults( int theBandNo,
|
||||
int theBinCount,
|
||||
double theMinimum, double theMaximum,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
{
|
||||
QgsRasterHistogram myHistogram;
|
||||
myHistogram.bandNumber = theBandNo;
|
||||
myHistogram.minimum = theMinimum;
|
||||
myHistogram.maximum = theMaximum;
|
||||
myHistogram.includeOutOfRange = theIncludeOutOfRange;
|
||||
|
||||
int mySrcDataType = srcDataType( theBandNo );
|
||||
|
||||
if ( qIsNaN( myHistogram.minimum ) )
|
||||
{
|
||||
if ( mySrcDataType == QgsRasterDataProvider::Byte )
|
||||
{
|
||||
myHistogram.minimum = 0; // see histogram() for shift for rounding
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need statistcs -> avoid histogramDefaults in hasHistogram if possible
|
||||
// TODO: use approximated statistics if aproximated histogram is requested
|
||||
// (theSampleSize > 0)
|
||||
QgsRasterBandStats stats = bandStatistics( theBandNo, theExtent, theSampleSize );
|
||||
myHistogram.minimum = stats.minimumValue;
|
||||
}
|
||||
}
|
||||
if ( qIsNaN( myHistogram.maximum ) )
|
||||
{
|
||||
if ( mySrcDataType == QgsRasterDataProvider::Byte )
|
||||
{
|
||||
myHistogram.maximum = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsRasterBandStats stats = bandStatistics( theBandNo, theExtent, theSampleSize );
|
||||
myHistogram.maximum = stats.maximumValue;
|
||||
}
|
||||
}
|
||||
|
||||
QgsRectangle myExtent = theExtent.isEmpty() ? extent() : theExtent;
|
||||
myHistogram.extent = myExtent;
|
||||
|
||||
if ( theSampleSize > 0 )
|
||||
{
|
||||
// Calc resolution from theSampleSize
|
||||
double xRes, yRes;
|
||||
xRes = yRes = sqrt(( myExtent.width() * myExtent.height() ) / theSampleSize );
|
||||
|
||||
// But limit by physical resolution
|
||||
if ( capabilities() & Size )
|
||||
{
|
||||
double srcXRes = extent().width() / xSize();
|
||||
double srcYRes = extent().height() / ySize();
|
||||
if ( xRes < srcXRes ) xRes = srcXRes;
|
||||
if ( yRes < srcYRes ) yRes = srcYRes;
|
||||
}
|
||||
QgsDebugMsg( QString( "xRes = %1 yRes = %2" ).arg( xRes ).arg( yRes ) );
|
||||
|
||||
myHistogram.width = static_cast <int>( myExtent.width() / xRes );
|
||||
myHistogram.height = static_cast <int>( myExtent.height() / yRes );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( capabilities() & Size )
|
||||
{
|
||||
myHistogram.width = xSize();
|
||||
myHistogram.height = ySize();
|
||||
}
|
||||
else
|
||||
{
|
||||
myHistogram.width = 1000;
|
||||
myHistogram.height = 1000;
|
||||
}
|
||||
}
|
||||
QgsDebugMsg( QString( "myHistogram.width = %1 myHistogram.height = %2" ).arg( myHistogram.width ).arg( myHistogram.height ) );
|
||||
|
||||
int myBinCount = theBinCount;
|
||||
if ( myBinCount == 0 )
|
||||
{
|
||||
if ( mySrcDataType == QgsRasterDataProvider::Byte )
|
||||
{
|
||||
myBinCount = 256; // Cannot store more values in byte
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is no best default value, to display something reasonable in histogram chart, binCount should be small, OTOH, to get precise data for cumulative cut, the number should be big. Because it is easier to define fixed lower value for the chart, we calc optimum binCount for higher resolution (to avoid calculating that where histogram() is used. In any any case, it does not make sense to use more than width*height;
|
||||
myBinCount = myHistogram.width * myHistogram.height;
|
||||
if ( myBinCount > 1000 ) myBinCount = 1000;
|
||||
}
|
||||
}
|
||||
myHistogram.binCount = myBinCount;
|
||||
QgsDebugMsg( QString( "myHistogram.binCount = %1" ).arg( myHistogram.binCount ) );
|
||||
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
bool QgsRasterDataProvider::hasHistogram( int theBandNo,
|
||||
int theBinCount,
|
||||
double theMinimum, double theMaximum,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
{
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
|
||||
// histogramDefaults() needs statistics if theMinimum or theMaximum is NaN ->
|
||||
// do other checks which dont need statistics before histogramDefaults()
|
||||
if ( mHistograms.size() == 0 ) return false;
|
||||
|
||||
QgsRasterHistogram myHistogram = histogramDefaults( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
|
||||
|
||||
foreach( QgsRasterHistogram histogram, mHistograms )
|
||||
{
|
||||
if ( histogram == myHistogram )
|
||||
{
|
||||
QgsDebugMsg( "Has cached histogram." );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo,
|
||||
int theBinCount,
|
||||
double theMinimum, double theMaximum,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
{
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
|
||||
|
||||
QgsRasterHistogram myHistogram = histogramDefaults( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
|
||||
|
||||
// Find cached
|
||||
foreach( QgsRasterHistogram histogram, mHistograms )
|
||||
{
|
||||
if ( histogram == myHistogram )
|
||||
{
|
||||
QgsDebugMsg( "Using cached histogram." );
|
||||
return histogram;
|
||||
}
|
||||
}
|
||||
|
||||
int myBinCount = myHistogram.binCount;
|
||||
int myWidth = myHistogram.width;
|
||||
int myHeight = myHistogram.height;
|
||||
QgsRectangle myExtent = myHistogram.extent;
|
||||
myHistogram.histogramVector.resize( myBinCount );
|
||||
|
||||
double myNoDataValue = noDataValue();
|
||||
int myDataType = dataType( theBandNo );
|
||||
|
||||
if ( xBlockSize() == 0 || yBlockSize() == 0 ) // should not happen
|
||||
{
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
int myNXBlocks = ( myWidth + xBlockSize() - 1 ) / xBlockSize();
|
||||
int myNYBlocks = ( myHeight + yBlockSize() - 1 ) / yBlockSize();
|
||||
|
||||
void *myData = CPLMalloc( xBlockSize() * yBlockSize() * ( dataTypeSize( theBandNo ) / 8 ) );
|
||||
|
||||
double myXRes = myExtent.width() / myWidth;
|
||||
double myYRes = myExtent.height() / myHeight;
|
||||
|
||||
double myMinimum = myHistogram.minimum;
|
||||
double myMaximum = myHistogram.maximum;
|
||||
|
||||
// To avoid rounding errors
|
||||
// TODO: check this
|
||||
double myerval = ( myMaximum - myMinimum ) / myHistogram.binCount;
|
||||
myMinimum -= 0.1 * myerval;
|
||||
myMaximum += 0.1 * myerval;
|
||||
|
||||
QgsDebugMsg( QString( "binCount = %1 myMinimum = %2 myMaximum = %3" ).arg( myHistogram.binCount ).arg( myMinimum ).arg( myMaximum ) );
|
||||
|
||||
double myBinSize = ( myMaximum - myMinimum ) / myBinCount;
|
||||
|
||||
// TODO: progress signals
|
||||
for ( int myYBlock = 0; myYBlock < myNYBlocks; myYBlock++ )
|
||||
{
|
||||
for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
|
||||
{
|
||||
int myBlockWidth = qMin( xBlockSize(), myWidth - myXBlock * xBlockSize() );
|
||||
int myBlockHeight = qMin( yBlockSize(), myHeight - myYBlock * yBlockSize() );
|
||||
|
||||
double xmin = myExtent.xMinimum() + myXBlock * xBlockSize() * myXRes;
|
||||
double xmax = xmin + myBlockWidth * myXRes;
|
||||
double ymin = myExtent.yMaximum() - myYBlock * yBlockSize() * myYRes;
|
||||
double ymax = ymin - myBlockHeight * myYRes;
|
||||
|
||||
QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );
|
||||
|
||||
readBlock( theBandNo, myPartExtent, myBlockWidth, myBlockHeight, myData );
|
||||
|
||||
// Collect the histogram counts.
|
||||
for ( int myY = 0; myY < myBlockHeight; myY++ )
|
||||
{
|
||||
for ( int myX = 0; myX < myBlockWidth; myX++ )
|
||||
{
|
||||
double myValue = readValue( myData, myDataType, myX + ( myY * myBlockWidth ) );
|
||||
//QgsDebugMsg ( QString ( "%1 %2 value %3" ).arg (myX).arg(myY).arg( myValue ) );
|
||||
|
||||
if ( mValidNoDataValue && ( qAbs( myValue - myNoDataValue ) <= TINY_VALUE ) )
|
||||
{
|
||||
continue; // NULL
|
||||
}
|
||||
|
||||
int myBinIndex = static_cast <int>( floor(( myValue - myMinimum ) / myBinSize ) ) ;
|
||||
//QgsDebugMsg( QString( "myValue = %1 myBinIndex = %2" ).arg( myValue ).arg( myBinIndex ) );
|
||||
|
||||
if (( myBinIndex < 0 || myBinIndex > ( myBinCount - 1 ) ) && !theIncludeOutOfRange )
|
||||
{
|
||||
continue;
|
||||
@ -579,14 +827,23 @@ QgsRasterHistogram QgsRasterDataProvider::histogram( int theBandNo,
|
||||
myHistogram.nonNullCount++;
|
||||
}
|
||||
}
|
||||
} //end of column wise loop
|
||||
} //end of row wise loop
|
||||
}
|
||||
}
|
||||
|
||||
CPLFree( myData );
|
||||
|
||||
myHistogram.valid = true;
|
||||
mHistograms.append( myHistogram );
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
QString hist;
|
||||
for ( int i = 0; i < qMin( myHistogram.histogramVector.size(), 500 ); i++ )
|
||||
{
|
||||
hist += QString::number( myHistogram.histogramVector.value( i ) ) + " ";
|
||||
}
|
||||
QgsDebugMsg( "Histogram (max first 500 bins): " + hist );
|
||||
#endif
|
||||
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
|
@ -317,20 +317,31 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
|
||||
|
||||
/** \brief Get histogram. Histograms are cached in providers.
|
||||
* @param theBandNo The band (number).
|
||||
* @param theMinimum Minimum value.
|
||||
* @param theMaximum Maximum value.
|
||||
* @param theBinCount Number of bins (intervals,buckets). If 0, the number of bins is decided automaticaly according to data type, raster size etc.
|
||||
* @param theMinimum Minimum value, if NaN, raster minimum value will be used.
|
||||
* @param theMaximum Maximum value, if NaN, raster minimum value will be used.
|
||||
* @param theExtent Extent used to calc histogram, if empty, whole raster extent is used.
|
||||
* @param theSampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
|
||||
* @return Vector of non NULL cell counts for each bin.
|
||||
*/
|
||||
virtual QgsRasterHistogram histogram( int theBandNo,
|
||||
double theMinimum, double theMaximum,
|
||||
int theBinCount = 0,
|
||||
double theMinimum = std::numeric_limits<double>::quiet_NaN(),
|
||||
double theMaximum = std::numeric_limits<double>::quiet_NaN(),
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0,
|
||||
bool theIncludeOutOfRange = false );
|
||||
|
||||
/** \brief Returns true if histogram is available (cached, already calculated), the parameters are the same as in histogram() */
|
||||
virtual bool hasHistogram( int theBandNo,
|
||||
int theBinCount = 0,
|
||||
double theMinimum = std::numeric_limits<double>::quiet_NaN(),
|
||||
double theMaximum = std::numeric_limits<double>::quiet_NaN(),
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0,
|
||||
bool theIncludeOutOfRange = false );
|
||||
|
||||
|
||||
/** \brief Create pyramid overviews */
|
||||
virtual QString buildPyramids( const QList<QgsRasterPyramid> & thePyramidList,
|
||||
const QString & theResamplingMethod = "NEAREST",
|
||||
@ -347,7 +358,17 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
|
||||
/** 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 );
|
||||
//virtual QgsRasterBandStats bandStatistics( int theBandNo );
|
||||
|
||||
/** \brief Get band statistics.
|
||||
* @param theBandNo The band (number).
|
||||
* @param theExtent Extent used to calc histogram, if empty, whole raster extent is used.
|
||||
* @param theSampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
|
||||
* @return Band statistics.
|
||||
*/
|
||||
virtual QgsRasterBandStats bandStatistics( int theBandNo,
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0 );
|
||||
|
||||
/** \brief helper function to create zero padded band names */
|
||||
QString generateBandName( int theBandNumber ) const
|
||||
@ -521,8 +542,25 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
|
||||
|
||||
QgsRectangle mExtent;
|
||||
|
||||
/** \brief List of cached statistics, all bands mixed */
|
||||
QList <QgsRasterBandStats> mStatistics;
|
||||
|
||||
/** \brief List of cached histograms, all bands mixed */
|
||||
QList <QgsRasterHistogram> mHistograms;
|
||||
};
|
||||
|
||||
/** Fill in histogram defaults if not specified */
|
||||
virtual QgsRasterHistogram histogramDefaults( int theBandNo,
|
||||
int theBinCount = 0,
|
||||
double theMinimum = std::numeric_limits<double>::quiet_NaN(),
|
||||
double theMaximum = std::numeric_limits<double>::quiet_NaN(),
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0,
|
||||
bool theIncludeOutOfRange = false );
|
||||
|
||||
/** Fill in statistics defaults if not specified */
|
||||
virtual QgsRasterBandStats statisticsDefaults( int theBandNo,
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theBinCount = 0 );
|
||||
|
||||
};
|
||||
#endif
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <limits>
|
||||
|
||||
#include "qgscolorrampshader.h"
|
||||
#include "qgsrectangle.h"
|
||||
|
||||
/** \ingroup core
|
||||
* The RasterBandStats struct is a container for statistics about a single
|
||||
* raster band.
|
||||
@ -31,8 +33,6 @@
|
||||
class CORE_EXPORT QgsRasterBandStats
|
||||
{
|
||||
public:
|
||||
typedef QVector<int> HistogramVector;
|
||||
|
||||
QgsRasterBandStats()
|
||||
{
|
||||
bandName = "";
|
||||
@ -45,9 +45,17 @@ class CORE_EXPORT QgsRasterBandStats
|
||||
stdDev = 0.0;
|
||||
sum = 0.0;
|
||||
elementCount = 0;
|
||||
histogramVector = new HistogramVector();
|
||||
isHistogramEstimated = false;
|
||||
isHistogramOutOfRange = false;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
/*! Compares region, size etc. not collected statistics */
|
||||
bool operator==( const QgsRasterBandStats &s ) const
|
||||
{
|
||||
return ( s.bandNumber == bandNumber &&
|
||||
s.extent == extent &&
|
||||
s.width == width &&
|
||||
s.height == height );
|
||||
}
|
||||
|
||||
/** \brief The name of the band that these stats belong to. */
|
||||
@ -63,15 +71,6 @@ class CORE_EXPORT QgsRasterBandStats
|
||||
* TODO: check if NO_DATA are excluded!*/
|
||||
int elementCount;
|
||||
|
||||
/** \brief whteher histogram values are estimated or completely calculated */
|
||||
bool isHistogramEstimated;
|
||||
|
||||
/** whehter histogram compuation should include out of range values */
|
||||
bool isHistogramOutOfRange;
|
||||
|
||||
/** \brief Store the histogram for a given layer */
|
||||
HistogramVector * histogramVector;
|
||||
|
||||
/** \brief The maximum cell value in the raster band. NO_DATA values
|
||||
* are ignored. This does not use the gdal GetMaximmum function. */
|
||||
double maximumValue;
|
||||
@ -98,5 +97,14 @@ class CORE_EXPORT QgsRasterBandStats
|
||||
|
||||
/** \brief The sum of the squares. Used to calculate standard deviation. */
|
||||
double sumOfSquares;
|
||||
|
||||
/** \brief Number of columns used to calc statistics */
|
||||
int width;
|
||||
|
||||
/** \brief Number of rows used to calc statistics */
|
||||
int height;
|
||||
|
||||
/** \brief Extent used to calc statistics */
|
||||
QgsRectangle extent;
|
||||
};
|
||||
#endif
|
||||
|
@ -27,7 +27,7 @@
|
||||
* The QgsRasterHistogram is a container for histogram of a single raster band.
|
||||
* It is used to cache computed histograms in raster providers.
|
||||
*/
|
||||
class CORE_EXPORT QgsRasterHistogram
|
||||
class CORE_EXPORT QgsRasterHistogram
|
||||
{
|
||||
public:
|
||||
typedef QVector<int> HistogramVector;
|
||||
@ -37,7 +37,6 @@ class CORE_EXPORT QgsRasterHistogram
|
||||
bandNumber = 0;
|
||||
binCount = 0;
|
||||
nonNullCount = 0;
|
||||
//sampleSize = 0;
|
||||
includeOutOfRange = false;
|
||||
maximum = 0;
|
||||
minimum = 0;
|
||||
@ -46,18 +45,28 @@ class CORE_EXPORT QgsRasterHistogram
|
||||
valid = false;
|
||||
}
|
||||
|
||||
/*! Compares region, size etc. not histogram itself */
|
||||
bool operator==( const QgsRasterHistogram &h ) const
|
||||
{
|
||||
return ( h.bandNumber == bandNumber &&
|
||||
h.binCount == binCount &&
|
||||
h.includeOutOfRange == includeOutOfRange &&
|
||||
h.maximum == maximum &&
|
||||
h.minimum == minimum &&
|
||||
h.extent == extent &&
|
||||
h.width == width &&
|
||||
h.height == height );
|
||||
}
|
||||
|
||||
/** \brief The gdal band number (starts at 1)*/
|
||||
int bandNumber;
|
||||
|
||||
/** \brief Number of bins (intervals,buckets) in histogram. */
|
||||
int binCount;
|
||||
|
||||
/** \brief The number of non NULL cells used to calculate histogram. */
|
||||
int nonNullCount;
|
||||
|
||||
/** \brief Approximate number of cells used to calc histogram. Approximately
|
||||
* width * height. */
|
||||
//int sampleSize;
|
||||
|
||||
/** \brief Whether histogram includes out of range values (in first and last bin) */
|
||||
bool includeOutOfRange;
|
||||
|
||||
|
@ -1729,7 +1729,6 @@ void QgsRasterLayer::setDataProvider( QString const & provider )
|
||||
myRasterBandStats.bandName = mDataProvider->generateBandName( i );
|
||||
myRasterBandStats.bandNumber = i;
|
||||
myRasterBandStats.statsGathered = false;
|
||||
myRasterBandStats.histogramVector->clear();
|
||||
//Store the default color table
|
||||
//readColorTable( i, &myRasterBandStats.colorTable );
|
||||
QList<QgsColorRampShader::ColorRampItem> ct;
|
||||
|
@ -215,7 +215,9 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
|
||||
myIteratorInt <= myBandCountInt;
|
||||
++myIteratorInt )
|
||||
{
|
||||
if ( ! mRasterLayer->hasCachedHistogram( myIteratorInt, BINCOUNT ) )
|
||||
//if ( ! mRasterLayer->hasCachedHistogram( myIteratorInt, BINCOUNT ) )
|
||||
int sampleSize = 250000; // number of sample cells
|
||||
if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, BINCOUNT, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
|
||||
{
|
||||
QgsDebugMsg( QString( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
|
||||
return false;
|
||||
@ -232,7 +234,9 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
|
||||
myIteratorInt <= myBandCountInt;
|
||||
++myIteratorInt )
|
||||
{
|
||||
mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
|
||||
//mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
|
||||
int sampleSize = 250000; // number of sample cells
|
||||
mRasterLayer->dataProvider()->histogram( myIteratorInt, BINCOUNT, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
|
||||
}
|
||||
|
||||
disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
|
||||
@ -401,6 +405,9 @@ void QgsRasterHistogramWidget::refreshHistogram()
|
||||
}
|
||||
QgsRasterBandStats myRasterBandStats = mRasterLayer->bandStatistics( myIteratorInt );
|
||||
// mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
|
||||
int sampleSize = 250000; // number of sample cells
|
||||
QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, BINCOUNT, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
|
||||
|
||||
QwtPlotCurve * mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
|
||||
mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
|
||||
mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
|
||||
@ -425,7 +432,8 @@ void QgsRasterHistogramWidget::refreshHistogram()
|
||||
|
||||
for ( int myBin = 0; myBin < BINCOUNT; myBin++ )
|
||||
{
|
||||
int myBinValue = myRasterBandStats.histogramVector->at( myBin );
|
||||
//int myBinValue = myRasterBandStats.histogramVector->at( myBin );
|
||||
int myBinValue = myHistogram.histogramVector.at( myBin );
|
||||
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
|
||||
data << QPointF( myBinX, myBinValue );
|
||||
#else
|
||||
|
@ -78,29 +78,30 @@ bool QgsRasterRendererWidget::bandMinMax( LoadMinMaxAlgo loadAlgo, int band, dou
|
||||
else if ( loadAlgo == CumulativeCut )
|
||||
{
|
||||
// Currently 2 - 98% cumulative pixel count cut
|
||||
bool myIgnoreOutOfRangeFlag = true;
|
||||
bool myThoroughBandScanFlag = false;
|
||||
mRasterLayer->populateHistogram( band, RASTER_HISTOGRAM_BINS, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
|
||||
//QgsRasterBandStats myRasterBandStats = mRasterLayer->bandStatistics( band );
|
||||
|
||||
QgsRasterBandStats myRasterBandStats = mRasterLayer->bandStatistics( band );
|
||||
double myBinXStep = myRasterBandStats.range / RASTER_HISTOGRAM_BINS;
|
||||
int sampleSize = 250000; // number of sample cells
|
||||
QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( band, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
|
||||
|
||||
double myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
|
||||
int myCount = 0;
|
||||
int myMinCount = ( int ) qRound( 0.02 * myRasterBandStats.elementCount );
|
||||
int myMaxCount = ( int ) qRound( 0.98 * myRasterBandStats.elementCount );
|
||||
int myMinCount = ( int ) qRound( 0.02 * myHistogram.nonNullCount );
|
||||
int myMaxCount = ( int ) qRound( 0.98 * myHistogram.nonNullCount );
|
||||
bool myMinFound = false;
|
||||
QgsDebugMsg( QString( "RASTER_HISTOGRAM_BINS = %1 range = %2 minimumValue = %3 myBinXStep = %4" ).arg( RASTER_HISTOGRAM_BINS ).arg( myRasterBandStats.range ).arg( myRasterBandStats.minimumValue ).arg( myBinXStep ) );
|
||||
for ( int myBin = 0; myBin < RASTER_HISTOGRAM_BINS; myBin++ )
|
||||
QgsDebugMsg( QString( "binCount = %1 minimum = %2 maximum = %3 myBinXStep = %4" ).arg( myHistogram.binCount ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myBinXStep ) );
|
||||
|
||||
for ( int myBin = 0; myBin < myHistogram.histogramVector.size(); myBin++ )
|
||||
{
|
||||
int myBinValue = myRasterBandStats.histogramVector->value( myBin );
|
||||
int myBinValue = myHistogram.histogramVector.value( myBin );
|
||||
myCount += myBinValue;
|
||||
if ( !myMinFound && myCount > myMinCount )
|
||||
{
|
||||
minMaxValues[0] = myRasterBandStats.minimumValue + myBin * myBinXStep;
|
||||
minMaxValues[0] = myHistogram.minimum + myBin * myBinXStep;
|
||||
myMinFound = true;
|
||||
}
|
||||
if ( myCount > myMaxCount )
|
||||
{
|
||||
minMaxValues[1] = myRasterBandStats.minimumValue + myBin * myBinXStep;
|
||||
minMaxValues[1] = myHistogram.minimum + myBin * myBinXStep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1243,124 +1243,212 @@ QStringList QgsGdalProvider::subLayers( GDALDatasetH dataset )
|
||||
return subLayers;
|
||||
}
|
||||
|
||||
bool QgsGdalProvider::hasCachedHistogram( int theBandNo, int theBinCount )
|
||||
bool QgsGdalProvider::hasHistogram( int theBandNo,
|
||||
int theBinCount,
|
||||
double theMinimum, double theMaximum,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
{
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
|
||||
|
||||
// First check if cached in mHistograms
|
||||
if ( QgsRasterDataProvider::hasHistogram( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsRasterHistogram myHistogram = histogramDefaults( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
|
||||
|
||||
// If not cached, check if supported by GDAL
|
||||
if ( myHistogram.extent != extent() )
|
||||
{
|
||||
QgsDebugMsg( "Not supported by GDAL." );
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsDebugMsg( "Looking for GDAL histogram xxxx" );
|
||||
|
||||
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
||||
if ( ! myGdalBand )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get default histogram with force=false to see if there is a cached histo
|
||||
double myMinVal, myMaxVal;
|
||||
int myBinCount;
|
||||
int *myHistogramArray = 0;
|
||||
|
||||
// TODO: GDALGetDefaultHistogram has no bIncludeOutOfRange and bApproxOK,
|
||||
// consider consequences
|
||||
CPLErr myError = GDALGetDefaultHistogram( myGdalBand, &myMinVal, &myMaxVal,
|
||||
&myBinCount, &myHistogramArray, false,
|
||||
NULL, NULL );
|
||||
|
||||
if ( myHistogramArray )
|
||||
VSIFree( myHistogramArray );
|
||||
|
||||
// if there was any error/warning assume the histogram is not valid or non-existent
|
||||
if ( myError != CE_None )
|
||||
{
|
||||
QgsDebugMsg( "Cannot get default GDAL histogram" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the cached histo has the same number of bins than requested
|
||||
if ( myBinCount != theBinCount )
|
||||
// This is fragile
|
||||
double myExpectedMinVal = myHistogram.minimum;
|
||||
double myExpectedMaxVal = myHistogram.maximum;
|
||||
|
||||
double dfHalfBucket = ( myExpectedMaxVal - myExpectedMinVal ) / ( 2 * myHistogram.binCount );
|
||||
myExpectedMinVal -= dfHalfBucket;
|
||||
myExpectedMaxVal += dfHalfBucket;
|
||||
|
||||
// min/max are stored as text in aux file => use threshold
|
||||
if ( myBinCount != myHistogram.binCount ||
|
||||
qAbs( myMinVal - myExpectedMinVal ) > qAbs( myExpectedMinVal ) / 10e6 ||
|
||||
qAbs( myMaxVal - myExpectedMaxVal ) > qAbs( myExpectedMaxVal ) / 10e6 )
|
||||
{
|
||||
QgsDebugMsg( QString( "Params do not match binCount: %1 x %2, minVal: %3 x %4, maxVal: %5 x %6" ).arg( myBinCount ).arg( myHistogram.binCount ).arg( myMinVal ).arg( myExpectedMinVal ).arg( myMaxVal ).arg( myExpectedMaxVal ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsDebugMsg( "GDAL has cached histogram" );
|
||||
|
||||
// This should be enough, possible call to histogram() should retrieve the histogram cached in GDAL
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & theBandStats, int theBinCount, bool theIgnoreOutOfRangeFlag, bool theHistogramEstimatedFlag )
|
||||
QgsRasterHistogram QgsGdalProvider::histogram( int theBandNo,
|
||||
int theBinCount,
|
||||
double theMinimum, double theMaximum,
|
||||
const QgsRectangle & theExtent,
|
||||
int theSampleSize,
|
||||
bool theIncludeOutOfRange )
|
||||
{
|
||||
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
||||
//QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo );
|
||||
//calculate the histogram for this band
|
||||
//we assume that it only needs to be calculated if the length of the histogram
|
||||
//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 == 0 ||
|
||||
theBandStats.histogramVector->size() != theBinCount ||
|
||||
theIgnoreOutOfRangeFlag != theBandStats.isHistogramOutOfRange ||
|
||||
theHistogramEstimatedFlag != theBandStats.isHistogramEstimated )
|
||||
{
|
||||
QgsDebugMsg( "Computing histogram" );
|
||||
theBandStats.histogramVector->clear();
|
||||
theBandStats.isHistogramEstimated = theHistogramEstimatedFlag;
|
||||
theBandStats.isHistogramOutOfRange = theIgnoreOutOfRangeFlag;
|
||||
int *myHistogramArray = new int[theBinCount];
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theBinCount = %2 theMinimum = %3 theMaximum = %4 theSampleSize = %5" ).arg( theBandNo ).arg( theBinCount ).arg( theMinimum ).arg( theMaximum ).arg( theSampleSize ) );
|
||||
|
||||
QgsGdalProgress myProg;
|
||||
myProg.type = ProgressHistogram;
|
||||
myProg.provider = this;
|
||||
QgsRasterHistogram myHistogram = histogramDefaults( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
|
||||
|
||||
// Find cached
|
||||
foreach( QgsRasterHistogram histogram, mHistograms )
|
||||
{
|
||||
if ( histogram == myHistogram )
|
||||
{
|
||||
QgsDebugMsg( "Using cached histogram." );
|
||||
return histogram;
|
||||
}
|
||||
}
|
||||
|
||||
if ( myHistogram.extent != extent() )
|
||||
{
|
||||
QgsDebugMsg( "Using generic histogram." );
|
||||
return QgsRasterDataProvider::histogram( theBandNo, theBinCount, theMinimum, theMaximum, theExtent, theSampleSize, theIncludeOutOfRange );
|
||||
}
|
||||
|
||||
QgsDebugMsg( "Computing GDAL histogram" );
|
||||
|
||||
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
||||
|
||||
int bApproxOK = false;
|
||||
if ( theSampleSize > 0 )
|
||||
{
|
||||
if (( xSize() * ySize() / theSampleSize ) > 2 ) // not perfect
|
||||
{
|
||||
bApproxOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
QgsGdalProgress myProg;
|
||||
myProg.type = ProgressHistogram;
|
||||
myProg.provider = this;
|
||||
|
||||
#if 0 // this is the old method
|
||||
|
||||
double myerval = ( theBandStats.maximumValue - theBandStats.minimumValue ) / theBinCount;
|
||||
GDALGetRasterHistogram( myGdalBand, theBandStats.minimumValue - 0.1*myerval,
|
||||
theBandStats.maximumValue + 0.1*myerval, theBinCount, myHistogramArray,
|
||||
theIgnoreOutOfRangeFlag, theHistogramEstimatedFlag, progressCallback,
|
||||
&myProg ); //this is the arg for our custom gdal progress callback
|
||||
double myerval = ( theBandStats.maximumValue - theBandStats.minimumValue ) / theBinCount;
|
||||
GDALGetRasterHistogram( myGdalBand, theBandStats.minimumValue - 0.1*myerval,
|
||||
theBandStats.maximumValue + 0.1*myerval, theBinCount, myHistogramArray,
|
||||
theIgnoreOutOfRangeFlag, theHistogramEstimatedFlag, progressCallback,
|
||||
&myProg ); //this is the arg for our custom gdal progress callback
|
||||
|
||||
#else // this is the new method, which gets a "Default" histogram
|
||||
|
||||
// calculate min/max like in GDALRasterBand::GetDefaultHistogram, but don't call it directly
|
||||
// because there is no bApproxOK argument - that is lacking from the API
|
||||
double myMinVal, myMaxVal;
|
||||
const char* pszPixelType = GDALGetMetadataItem( myGdalBand, "PIXELTYPE", "IMAGE_STRUCTURE" );
|
||||
int bSignedByte = ( pszPixelType != NULL && EQUAL( pszPixelType, "SIGNEDBYTE" ) );
|
||||
// calculate min/max like in GDALRasterBand::GetDefaultHistogram, but don't call it directly
|
||||
// because there is no bApproxOK argument - that is lacking from the API
|
||||
|
||||
if ( GDALGetRasterDataType( myGdalBand ) == GDT_Byte && !bSignedByte )
|
||||
{
|
||||
myMinVal = -0.5;
|
||||
myMaxVal = 255.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLErr eErr = CE_Failure;
|
||||
double dfHalfBucket = 0;
|
||||
eErr = GDALGetRasterStatistics( myGdalBand, TRUE, TRUE, &myMinVal, &myMaxVal, NULL, NULL );
|
||||
if ( eErr != CE_None )
|
||||
{
|
||||
delete [] myHistogramArray;
|
||||
return;
|
||||
}
|
||||
dfHalfBucket = ( myMaxVal - myMinVal ) / ( 2 * theBinCount );
|
||||
myMinVal -= dfHalfBucket;
|
||||
myMaxVal += dfHalfBucket;
|
||||
}
|
||||
// Min/max, if not specified, are set by histogramDefaults, it does not
|
||||
// set however min/max shifted to avoid rounding errors
|
||||
|
||||
CPLErr myError = GDALGetRasterHistogram( myGdalBand, myMinVal, myMaxVal,
|
||||
theBinCount, myHistogramArray,
|
||||
theIgnoreOutOfRangeFlag, theHistogramEstimatedFlag, progressCallback,
|
||||
&myProg ); //this is the arg for our custom gdal progress callback
|
||||
if ( myError != CE_None )
|
||||
double myMinVal = myHistogram.minimum;
|
||||
double myMaxVal = myHistogram.maximum;
|
||||
|
||||
double dfHalfBucket = ( myMaxVal - myMinVal ) / ( 2 * myHistogram.binCount );
|
||||
myMinVal -= dfHalfBucket;
|
||||
myMaxVal += dfHalfBucket;
|
||||
|
||||
/*
|
||||
const char* pszPixelType = GDALGetMetadataItem( myGdalBand, "PIXELTYPE", "IMAGE_STRUCTURE" );
|
||||
int bSignedByte = ( pszPixelType != NULL && EQUAL( pszPixelType, "SIGNEDBYTE" ) );
|
||||
|
||||
if ( GDALGetRasterDataType( myGdalBand ) == GDT_Byte && !bSignedByte )
|
||||
{
|
||||
myMinVal = -0.5;
|
||||
myMaxVal = 255.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLErr eErr = CE_Failure;
|
||||
double dfHalfBucket = 0;
|
||||
eErr = GDALGetRasterStatistics( myGdalBand, TRUE, TRUE, &myMinVal, &myMaxVal, NULL, NULL );
|
||||
if ( eErr != CE_None )
|
||||
{
|
||||
delete [] myHistogramArray;
|
||||
return;
|
||||
}
|
||||
dfHalfBucket = ( myMaxVal - myMinVal ) / ( 2 * theBinCount );
|
||||
myMinVal -= dfHalfBucket;
|
||||
myMaxVal += dfHalfBucket;
|
||||
}
|
||||
*/
|
||||
|
||||
int *myHistogramArray = new int[myHistogram.binCount];
|
||||
CPLErr myError = GDALGetRasterHistogram( myGdalBand, myMinVal, myMaxVal,
|
||||
myHistogram.binCount, myHistogramArray,
|
||||
theIncludeOutOfRange, bApproxOK, progressCallback,
|
||||
&myProg ); //this is the arg for our custom gdal progress callback
|
||||
if ( myError != CE_None )
|
||||
{
|
||||
QgsDebugMsg( "Cannot get histogram" );
|
||||
delete [] myHistogramArray;
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for ( int myBin = 0; myBin < theBinCount; myBin++ )
|
||||
for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
|
||||
{
|
||||
if ( myHistogramArray[myBin] < 0 ) //can't have less than 0 pixels of any value
|
||||
{
|
||||
if ( myHistogramArray[myBin] < 0 ) //can't have less than 0 pixels of any value
|
||||
{
|
||||
theBandStats.histogramVector->push_back( 0 );
|
||||
// QgsDebugMsg( "Added 0 to histogram vector as freq was negative!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
theBandStats.histogramVector->push_back( myHistogramArray[myBin] );
|
||||
// QgsDebugMsg( "Added " + QString::number( myHistogramArray[myBin] ) + " to histogram vector" );
|
||||
}
|
||||
myHistogram.histogramVector.push_back( 0 );
|
||||
// QgsDebugMsg( "Added 0 to histogram vector as freq was negative!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
myHistogram.histogramVector.push_back( myHistogramArray[myBin] );
|
||||
myHistogram.nonNullCount += myHistogramArray[myBin];
|
||||
// QgsDebugMsg( "Added " + QString::number( myHistogramArray[myBin] ) + " to histogram vector" );
|
||||
}
|
||||
delete [] myHistogramArray;
|
||||
|
||||
|
||||
}
|
||||
QgsDebugMsg( ">>>>> Histogram vector now contains " + QString::number( theBandStats.histogramVector->size() ) +
|
||||
" elements" );
|
||||
|
||||
myHistogram.valid = true;
|
||||
|
||||
delete [] myHistogramArray;
|
||||
|
||||
QgsDebugMsg( ">>>>> Histogram vector now contains " + QString::number( myHistogram.histogramVector.size() ) + " elements" );
|
||||
|
||||
mHistograms.append( myHistogram );
|
||||
return myHistogram;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1913,14 +2001,47 @@ QGISEXTERN bool isValidRasterFileName( QString const & theFileNameQString, QStri
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo )
|
||||
QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo, const QgsRectangle & theExtent, int theSampleSize )
|
||||
{
|
||||
QgsDebugMsg( QString( "theBandNo = %1 theSampleSize = %2" ).arg( theBandNo ).arg( theSampleSize ) );
|
||||
// Currently there is no API in GDAL to collect statistics of specified extent
|
||||
// or with defined sample size. We check first if we have cached stats, if not,
|
||||
// and it is not possible to use GDAL we call generic provider method,
|
||||
// otherwise we use GDAL (faster, cache)
|
||||
|
||||
QgsRasterBandStats myRasterBandStats = statisticsDefaults( theBandNo, theExtent, theSampleSize );
|
||||
|
||||
foreach( QgsRasterBandStats stats, mStatistics )
|
||||
{
|
||||
if ( stats == myRasterBandStats )
|
||||
{
|
||||
QgsDebugMsg( "Using cached statistics." );
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
if ( myRasterBandStats.extent != extent() )
|
||||
{
|
||||
QgsDebugMsg( "Using generic statistics." );
|
||||
return QgsRasterDataProvider::bandStatistics( theBandNo, theExtent, theSampleSize );
|
||||
}
|
||||
|
||||
QgsDebugMsg( "Using GDAL statistics." );
|
||||
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
|
||||
QgsRasterBandStats myRasterBandStats;
|
||||
// int bApproxOK = true;
|
||||
int bApproxOK = false; //as we asked for stats, don't get approx values
|
||||
|
||||
//int bApproxOK = false; //as we asked for stats, don't get approx values
|
||||
// GDAL does not have sample size parameter in API, just bApproxOK or not,
|
||||
// we decide if approximation should be used according to
|
||||
// total size / sample size ration
|
||||
int bApproxOK = false;
|
||||
if ( theSampleSize > 0 )
|
||||
{
|
||||
if (( xSize() * ySize() / theSampleSize ) > 2 ) // not perfect
|
||||
{
|
||||
bApproxOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
double pdfMin;
|
||||
double pdfMax;
|
||||
double pdfMean;
|
||||
@ -1972,6 +2093,7 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo )
|
||||
|
||||
}
|
||||
|
||||
mStatistics.append( myRasterBandStats );
|
||||
return myRasterBandStats;
|
||||
|
||||
} // QgsGdalProvider::bandStatistics
|
||||
|
@ -244,15 +244,25 @@ class QgsGdalProvider : public QgsRasterDataProvider
|
||||
@note overloads virtual method from QgsRasterProvider::bandStatistics
|
||||
|
||||
*/
|
||||
QgsRasterBandStats bandStatistics( int theBandNo );
|
||||
QgsRasterBandStats bandStatistics( int theBandNo,
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0 );
|
||||
|
||||
bool hasCachedHistogram( int theBandNoInt, int theBinCountInt = RASTER_HISTOGRAM_BINS );
|
||||
void populateHistogram( int theBandNoInt,
|
||||
QgsRasterBandStats & theBandStats,
|
||||
int theBinCountInt = RASTER_HISTOGRAM_BINS,
|
||||
bool theIgnoreOutOfRangeFlag = true,
|
||||
bool theThoroughBandScanFlag = false
|
||||
);
|
||||
bool hasHistogram( int theBandNo,
|
||||
int theBinCount = 0,
|
||||
double theMinimum = std::numeric_limits<double>::quiet_NaN(),
|
||||
double theMaximum = std::numeric_limits<double>::quiet_NaN(),
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0,
|
||||
bool theIncludeOutOfRange = false );
|
||||
|
||||
QgsRasterHistogram histogram( int theBandNo,
|
||||
int theBinCount = 0,
|
||||
double theMinimum = std::numeric_limits<double>::quiet_NaN(),
|
||||
double theMaximum = std::numeric_limits<double>::quiet_NaN(),
|
||||
const QgsRectangle & theExtent = QgsRectangle(),
|
||||
int theSampleSize = 0,
|
||||
bool theIncludeOutOfRange = false );
|
||||
|
||||
QString buildPyramids( const QList<QgsRasterPyramid> &,
|
||||
const QString & theResamplingMethod = "NEAREST",
|
||||
|
@ -443,33 +443,6 @@ QString QgsGrassRasterProvider::metadata()
|
||||
return myMetadata;
|
||||
}
|
||||
|
||||
void QgsGrassRasterProvider::populateHistogram( int theBandNoInt,
|
||||
QgsRasterBandStats & theBandStats,
|
||||
int theBinCount,
|
||||
bool theIgnoreOutOfRangeFlag,
|
||||
bool theHistogramEstimatedFlag )
|
||||
{
|
||||
Q_UNUSED( theBandNoInt );
|
||||
// TODO: we could either implement it in QgsRasterDataProvider::populateHistogram
|
||||
// or use r.stats (see d.histogram)
|
||||
if ( theBandStats.histogramVector->size() != theBinCount ||
|
||||
theIgnoreOutOfRangeFlag != theBandStats.isHistogramOutOfRange ||
|
||||
theHistogramEstimatedFlag != theBandStats.isHistogramEstimated )
|
||||
{
|
||||
theBandStats.histogramVector->clear();
|
||||
theBandStats.isHistogramEstimated = theHistogramEstimatedFlag;
|
||||
theBandStats.isHistogramOutOfRange = theIgnoreOutOfRangeFlag;
|
||||
for ( int myBin = 0; myBin < theBinCount; myBin++ )
|
||||
{
|
||||
theBandStats.histogramVector->push_back( 0 );
|
||||
}
|
||||
}
|
||||
QgsDebugMsg( ">>>>> Histogram vector now contains " +
|
||||
QString::number( theBandStats.histogramVector->size() ) + " elements" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool QgsGrassRasterProvider::isValid()
|
||||
{
|
||||
return mValid;
|
||||
|
@ -230,13 +230,6 @@ class QgsGrassRasterProvider : public QgsRasterDataProvider
|
||||
void setImageCrs( QString const &crs )
|
||||
{ Q_UNUSED( crs ); }
|
||||
|
||||
void populateHistogram( int theBandNoInt,
|
||||
QgsRasterBandStats & theBandStats,
|
||||
int theBinCountInt = 256,
|
||||
bool theIgnoreOutOfRangeFlag = true,
|
||||
bool theThoroughBandScanFlag = false
|
||||
);
|
||||
|
||||
virtual QDateTime dataTimestamp() const;
|
||||
private:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user