Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[GUI] Raster histogram: restore behavior before #35465 changes
Commits 98261bc and ebdb546
for #35465 have changed the logic to compute the default number of bins
for the histogram. While appropriate in the context of #35465, that tends
to augment the number of bins, which is inappropriate for the raster histogram
chart, where beyond 1000, the chart becomes unreadable.

However I think the logic for plotting the raster histogram should probably be
revised, but I'm not clear how: ask the user for a number of bins and/or take
into account the width in pixels of the chart to determine the number of bins
(should that depend if the user has zoomed in... ?)
  • Loading branch information
rouault committed Sep 15, 2020
1 parent 4fe3d2f commit da71202
Showing 1 changed file with 56 additions and 9 deletions.
65 changes: 56 additions & 9 deletions src/gui/raster/qgsrasterhistogramwidget.cpp
Expand Up @@ -49,9 +49,7 @@
#include <time.h>
#endif

// this has been removed, now we let the provider/raster interface decide
// how many bins are suitable depending on data type and range
//#define RASTER_HISTOGRAM_BINS 256
constexpr int SAMPLE_SIZE = 250000; // number of sample cells

QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer *lyr, QWidget *parent )
: QgsMapLayerConfigWidget( lyr, nullptr, parent )
Expand Down Expand Up @@ -280,6 +278,52 @@ void QgsRasterHistogramWidget::btnHistoCompute_clicked()
refreshHistogram();
}

// Compute the number of bins
// Logic partially borrowed to QgsRasterInterface::initHistogram(),
// but with a limitation to 1000 bins. Otherwise the histogram will be
// unreadable (see https://github.com/qgis/QGIS/issues/38298)
// NOTE: the number of bins should probably be let to the user, and/or adaptative
// to the width in pixels of the chart.
static int getBinCount( QgsRasterInterface *rasterInterface,
int bandNo,
int sampleSize )
{
const Qgis::DataType mySrcDataType = rasterInterface->sourceDataType( bandNo );
const double statsMin = mySrcDataType == Qgis::Byte ? 0 :
rasterInterface->bandStatistics( bandNo, QgsRasterBandStats::Min, QgsRectangle(), sampleSize ).minimumValue;
const double statsMax = mySrcDataType == Qgis::Byte ? 255 :
rasterInterface->bandStatistics( bandNo, QgsRasterBandStats::Max, QgsRectangle(), sampleSize ).maximumValue;
const QgsRectangle extent( rasterInterface->extent() );

// Calc resolution from sampleSize
double xRes, yRes;
xRes = yRes = std::sqrt( ( static_cast<double>( extent.width( ) ) * extent.height() ) / sampleSize );

// But limit by physical resolution
if ( rasterInterface->capabilities() & QgsRasterInterface::Size )
{
const double srcXRes = extent.width() / rasterInterface->xSize();
const double srcYRes = extent.height() / rasterInterface->ySize();
if ( xRes < srcXRes ) xRes = srcXRes;
if ( yRes < srcYRes ) yRes = srcYRes;
}

const int histogramWidth = static_cast <int>( extent.width() / xRes );
const int histogramHeight = static_cast <int>( extent.height() / yRes );

int binCount = static_cast<int>( std::min( static_cast<qint64>( 1000 ),
static_cast<qint64>( histogramWidth ) * histogramHeight ) );

if ( mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 )
{
binCount = static_cast<int>( std::min( static_cast<qint64>( binCount ),
static_cast<qint64>( std::ceil( statsMax - statsMin + 1 ) ) ) );
}

return binCount;
}

bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
{

Expand All @@ -294,8 +338,9 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
int sampleSize = 250000; // number of sample cells
if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
int sampleSize = SAMPLE_SIZE; // number of sample cells
const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
{
QgsDebugMsg( QStringLiteral( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
return false;
Expand All @@ -314,8 +359,9 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
int sampleSize = 250000; // number of sample cells
mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
int sampleSize = SAMPLE_SIZE; // number of sample cells
const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
mRasterLayer->dataProvider()->histogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
}

// mHistogramProgress->hide();
Expand Down Expand Up @@ -480,12 +526,13 @@ void QgsRasterHistogramWidget::refreshHistogram()
continue;
}

int sampleSize = 250000; // number of sample cells
int sampleSize = SAMPLE_SIZE; // number of sample cells

std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );

QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );

QgsDebugMsg( QStringLiteral( "got raster histo for band %1 : min=%2 max=%3 count=%4" ).arg( myIteratorInt ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myHistogram.binCount ) );

Expand Down

0 comments on commit da71202

Please sign in to comment.