Skip to content
Permalink
Browse files

Merge pull request #1213 from etiennesky/histogram_bars

Histogram bars
  • Loading branch information
etiennesky committed Apr 17, 2014
2 parents a70a18d + 7399789 commit 2d737510d4ab93487a6187e2936cbde0cd752886
@@ -349,6 +349,16 @@ void QgsRasterInterface::initHistogram( QgsRasterHistogram &theHistogram,
// 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 = theHistogram.width * theHistogram.height;
if ( myBinCount > 1000 ) myBinCount = 1000;

// for Int16/Int32 make sure bin count <= actual range, because there is no sense in having
// bins at fractional values
if ( !mInput && (
mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 ) )
{
if ( myBinCount > theHistogram.maximum - theHistogram.minimum + 1 )
myBinCount = int( ceil( theHistogram.maximum - theHistogram.minimum + 1 ) );
}
}
}
theHistogram.binCount = myBinCount;
@@ -41,9 +41,14 @@
#include <qwt_plot_layout.h>
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
#include <qwt_plot_renderer.h>
#include <qwt_plot_histogram.h>
#else
#include "qwt5_histogram_item.h"
#endif

#define RASTER_HISTOGRAM_BINS 256
// 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

QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget *parent )
: QWidget( parent ),
@@ -69,9 +74,11 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget
// mHistoLoadApplyAll = settings.value( "/Raster/histogram/loadApplyAll", false ).toBool();
mHistoZoomToMinMax = settings.value( "/Raster/histogram/zoomToMinMax", false ).toBool();
mHistoUpdateStyleToMinMax = settings.value( "/Raster/histogram/updateStyleToMinMax", true ).toBool();
mHistoDrawLines = settings.value( "/Raster/histogram/drawLines", true ).toBool();
// mHistoShowBands = (HistoShowBands) settings.value( "/Raster/histogram/showBands", (int) ShowAll ).toInt();
mHistoShowBands = ShowAll;

bool isInt = true;
if ( true )
{
//band selector
@@ -81,6 +88,11 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget
++myIteratorInt )
{
cboHistoBand->addItem( mRasterLayer->bandName( myIteratorInt ) );
QGis::DataType mySrcDataType = mRasterLayer->dataProvider()->srcDataType( myIteratorInt );
if ( !( mySrcDataType == QGis::Byte ||
mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 ) )
isInt = false;
}

// histo min/max selectors
@@ -95,6 +107,7 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget
connect( leHistoMax, SIGNAL( editingFinished() ), this, SLOT( applyHistoMax() ) );

// histo actions
// TODO move/add options to qgis options dialog
QMenu* menu = new QMenu( this );
menu->setSeparatorsCollapsible( false );
btnHistoActions->setMenu( menu );
@@ -150,6 +163,29 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget
action->setChecked( mHistoShowBands == ShowSelected );
menu->addAction( action );

// display options
group = new QActionGroup( this );
group->setExclusive( false );
connect( group, SIGNAL( triggered( QAction* ) ), this, SLOT( histoActionTriggered( QAction* ) ) );
action = new QAction( tr( "Display" ), group );
action->setSeparator( true );
menu->addAction( action );
// should we plot as histogram instead of line plot? (int data only)
action = new QAction( "", group );
action->setData( QVariant( "Draw lines" ) );
if ( isInt )
{
action->setText( tr( "Draw as lines" ) );
action->setCheckable( true );
action->setChecked( mHistoDrawLines );
}
else
{
action->setText( tr( "Draw as lines (only int layers)" ) );
action->setEnabled( false );
}
menu->addAction( action );

// actions
action = new QAction( tr( "Actions" ), group );
action->setSeparator( true );
@@ -164,6 +200,7 @@ QgsRasterHistogramWidget::QgsRasterHistogramWidget( QgsRasterLayer* lyr, QWidget
menu->addAction( action );

// these actions have been disabled for api cleanup, restore them eventually
// TODO restore these in qgis 2.4
#if 0
// Load min/max needs 3 params (method, extent, accuracy), cannot put it in single item
action = new QAction( tr( "Load min/max" ), group );
@@ -234,14 +271,15 @@ void QgsRasterHistogramWidget::on_btnHistoCompute_clicked()
// Histogram computation can be called either by clicking the "Compute Histogram" button
// which is only visible if there is no cached histogram or by calling the
// "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
// to re-calculate the histogramif it has already been calculated
// to re-calculate the histogram if it has already been calculated
computeHistogram( true );
refreshHistogram();
}

bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
{
const int BINCOUNT = RASTER_HISTOGRAM_BINS;
QgsDebugMsg( "entered." );

//bool myIgnoreOutOfRangeFlag = true;
//bool myThoroughBandScanFlag = false;
int myBandCountInt = mRasterLayer->bandCount();
@@ -253,9 +291,8 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
//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 ) )
if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, 0, 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;
@@ -272,9 +309,8 @@ bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
//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 );
mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
}

disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
@@ -297,7 +333,6 @@ void QgsRasterHistogramWidget::refreshHistogram()
// bin in all selected layers, and the min. It then draws a scaled line between min
// and max - scaled to image height. 1 line drawn per selected band
//
const int BINCOUNT = RASTER_HISTOGRAM_BINS;
int myBandCountInt = mRasterLayer->bandCount();

QgsDebugMsg( "entered." );
@@ -309,11 +344,9 @@ void QgsRasterHistogramWidget::refreshHistogram()
return;
}

#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
// clear plot
mpPlot->detachItems();
#else
mpPlot->clear();
#endif

//ensure all children get removed
mpPlot->setAutoDelete( true );
mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
@@ -441,27 +474,68 @@ void QgsRasterHistogramWidget::refreshHistogram()
if ( ! mySelectedBands.contains( myIteratorInt ) )
continue;
}

int sampleSize = 250000; // number of sample cells
//QgsRasterBandStats myRasterBandStats = mRasterLayer->dataProvider()->bandStatistics( myIteratorInt );
// mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
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 );
mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );

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

QGis::DataType mySrcDataType = mRasterLayer->dataProvider()->srcDataType( myIteratorInt );
bool myDrawLines = true;
if ( ! mHistoDrawLines &&
( mySrcDataType == QGis::Byte ||
mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 ) )
{
myDrawLines = false;
}

QwtPlotCurve * mypCurve = 0;
if ( myDrawLines )
{
mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
//mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
}

#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QwtPlotHistogram * mypHisto = 0;
if ( ! myDrawLines )
{
mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( myIteratorInt ) );
mypHisto->setRenderHint( QwtPlotItem::RenderAntialiased );
//mypHisto->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
mypHisto->setPen( QPen( Qt::lightGray ) );
// this is needed in order to see the colors in the legend
mypHisto->setBrush( QBrush( mHistoColors.at( myIteratorInt ) ) );
}
#else
HistogramItem *mypHistoItem = 0;
if ( ! myDrawLines )
{
mypHistoItem = new HistogramItem( tr( "Band %1" ).arg( myIteratorInt ) );
mypHistoItem->setRenderHint( QwtPlotItem::RenderAntialiased );
mypHistoItem->setColor( mHistoColors.at( myIteratorInt ) );
}
#endif

#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QVector<QPointF> data;
QVector<QwtIntervalSample> dataHisto;
#else
QVector<double> myX2Data;
QVector<double> myY2Data;
// we safely assume that QT>=4.0 (min version is 4.7), therefore QwtArray is a QVector, so don't set size here
QwtArray<QwtDoubleInterval> intervalsHisto;
QwtArray<double> valuesHisto;

#endif

// calculate first bin x value and bin step size if not Byte data
if ( mRasterLayer->dataProvider()->srcDataType( myIteratorInt ) != QGis::Byte )
if ( mySrcDataType != QGis::Byte )
{
//myBinXStep = myRasterBandStats.range / BINCOUNT;
//myBinX = myRasterBandStats.minimumValue + myBinXStep / 2.0;
myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / BINCOUNT;
myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
myBinX = myHistogram.minimum + myBinXStep / 2.0;
}
else
@@ -470,24 +544,57 @@ void QgsRasterHistogramWidget::refreshHistogram()
myBinX = 0;
}

for ( int myBin = 0; myBin < BINCOUNT; myBin++ )
for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
{
//int myBinValue = myRasterBandStats.histogramVector->at( myBin );
int myBinValue = myHistogram.histogramVector.at( myBin );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
data << QPointF( myBinX, myBinValue );
if ( myDrawLines )
{
data << QPointF( myBinX, myBinValue );
}
else
{
dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
}
#else
myX2Data.append( double( myBinX ) );
myY2Data.append( double( myBinValue ) );
if ( myDrawLines )
{
myX2Data.append( double( myBinX ) );
myY2Data.append( double( myBinValue ) );
}
else
{
intervalsHisto.append( QwtDoubleInterval( myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 ) );
valuesHisto.append( double( myBinValue ) );
}
#endif
myBinX += myBinXStep;
}

#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
mypCurve->setSamples( data );
if ( myDrawLines )
{
mypCurve->setSamples( data );
mypCurve->attach( mpPlot );
}
else
{
mypHisto->setSamples( dataHisto );
mypHisto->attach( mpPlot );
}
#else
mypCurve->setData( myX2Data, myY2Data );
if ( myDrawLines )
{
mypCurve->setData( myX2Data, myY2Data );
mypCurve->attach( mpPlot );
}
else
{
mypHistoItem->setData( QwtIntervalData( intervalsHisto, valuesHisto ) );
mypHistoItem->attach( mpPlot );
}
#endif
mypCurve->attach( mpPlot );

if ( myFirstIteration || mHistoMin > myHistogram.minimum )
{
mHistoMin = myHistogram.minimum;
@@ -711,6 +818,14 @@ void QgsRasterHistogramWidget::histoAction( const QString actionName, bool actio
refreshHistogram();
return;
}
else if ( actionName == "Draw lines" )
{
mHistoDrawLines = actionFlag;
QSettings settings;
settings.setValue( "/Raster/histogram/drawLines", mHistoDrawLines );
on_btnHistoCompute_clicked(); // refresh
return;
}
#if 0
else if ( actionName == "Load apply all" )
{
@@ -114,6 +114,7 @@ class GUI_EXPORT QgsRasterHistogramWidget : public QWidget, private Ui::QgsRaste
bool mHistoShowMarkers;
bool mHistoZoomToMinMax;
bool mHistoUpdateStyleToMinMax;
bool mHistoDrawLines;
/* bool mHistoLoadApplyAll; */
HistoShowBands mHistoShowBands;
/** \brief Returns a list of selected bands in the histogram widget- or empty if there is no selection restriction. */

0 comments on commit 2d73751

Please sign in to comment.
You can’t perform that action at this time.