Skip to content

Commit

Permalink
verify that raster has cached histogram, compute on demand
Browse files Browse the repository at this point in the history
  • Loading branch information
etiennesky committed Jul 14, 2012
1 parent 6b474fe commit cf58d3e
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 31 deletions.
109 changes: 88 additions & 21 deletions src/app/qgsrasterlayerproperties.cpp
Expand Up @@ -330,8 +330,9 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanv
QMenu* menu = new QMenu( this );
menu->setSeparatorsCollapsible( false );
btnHistoActions->setMenu( menu );
QActionGroup* group;

QActionGroup* group = new QActionGroup( this );
group = new QActionGroup( this );
group->setExclusive( false );
connect( group, SIGNAL( triggered( QAction* ) ), this, SLOT( histoActionTriggered( QAction* ) ) );
QAction* action;
Expand Down Expand Up @@ -365,6 +366,13 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanv
action->setCheckable( true );
action->setChecked( true );
menu->addAction( action );

menu->addSeparator( );
group = new QActionGroup( this );
connect( group, SIGNAL( triggered( QAction* ) ), this, SLOT( histoActionTriggered( QAction* ) ) );
action = new QAction( tr( "Compute Histogram" ), group );
action->setData( QVariant( "Compute Histogram" ) );
menu->addAction( action );
}

// update based on lyr's current state
Expand Down Expand Up @@ -1194,16 +1202,85 @@ void QgsRasterLayerProperties::on_tabBar_currentChanged( int theTab )
}
}

void QgsRasterLayerProperties::refreshHistogram()
void QgsRasterLayerProperties::on_btnHistoCompute_clicked()
{
#if !defined(QWT_VERSION) || QWT_VERSION<0x060000
mpPlot->clear();
#endif
// mHistogramProgress->show();
// 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
QgsDebugMsg( "Entered" );
computeHistogram( true );
refreshHistogram();
}

bool QgsRasterLayerProperties::computeHistogram( bool forceComputeFlag )
{
const int BINCOUNT = RASTER_HISTOGRAM_BINS; // 256 - defined in qgsrasterdataprovider.h
bool myIgnoreOutOfRangeFlag = true;
bool myThoroughBandScanFlag = false;
int myBandCountInt = mRasterLayer->bandCount();

// if forceComputeFlag = false make sure raster has cached histogram, else return false
if ( ! forceComputeFlag )
{
for ( int myIteratorInt = 1;
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
if ( ! mRasterLayer->hasCachedHistogram( myIteratorInt, BINCOUNT ) )
{
QgsDebugMsg( QString( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
return false;
}
}
}

// compute histogram
stackedWidget2->setCurrentIndex( 1 );
connect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
QApplication::setOverrideCursor( Qt::WaitCursor );

for ( int myIteratorInt = 1;
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
}

disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
// mHistogramProgress->hide();
stackedWidget2->setCurrentIndex( 0 );
QApplication::restoreOverrideCursor();

return true;
}

void QgsRasterLayerProperties::refreshHistogram()
{
// Explanation:
// We use the gdal histogram creation routine is called for each selected
// layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
// the total number of cells that fit into the range defined by that bin.
//
// The graph routine below determines the greatest number of pixels in any given
// 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; // 256 - defined in qgsrasterdataprovider.h
int myBandCountInt = mRasterLayer->bandCount();

QgsDebugMsg( "entered." );

if ( ! computeHistogram( false ) )
{
QgsDebugMsg( QString( "raster does not have cached histo" ) );
stackedWidget2->setCurrentIndex( 2 );
return;
}

#if !defined(QWT_VERSION) || QWT_VERSION<0x060000
mpPlot->clear();
#endif
//ensure all children get removed
mpPlot->setAutoDelete( true );
mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
Expand All @@ -1216,20 +1293,6 @@ void QgsRasterLayerProperties::refreshHistogram()
// add a grid
QwtPlotGrid * myGrid = new QwtPlotGrid();
myGrid->attach( mpPlot );
// Explanation:
// We use the gdal histogram creation routine is called for each selected
// layer. Currently the hist is hardcoded
// to create 256 bins. Each bin stores the total number of cells that
// fit into the range defined by that bin.
//
// The graph routine below determines the greatest number of pixels in any given
// 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 = 256;
bool myIgnoreOutOfRangeFlag = true;
bool myThoroughBandScanFlag = false;
int myBandCountInt = mRasterLayer->bandCount();

// make colors list
mHistoColors.clear();
Expand Down Expand Up @@ -1327,7 +1390,7 @@ void QgsRasterLayerProperties::refreshHistogram()
++myIteratorInt )
{
QgsRasterBandStats myRasterBandStats = mRasterLayer->bandStatistics( myIteratorInt );
mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
// mRasterLayer->populateHistogram( myIteratorInt, BINCOUNT, myIgnoreOutOfRangeFlag, myThoroughBandScanFlag );
QwtPlotCurve * mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
Expand Down Expand Up @@ -1938,6 +2001,10 @@ void QgsRasterLayerProperties::histoActionTriggered( QAction* action )
updateHistoMarkers();
}
}
else if ( actionName == "Compute Histogram" )
{
on_btnHistoCompute_clicked();
}
else
{
return;
Expand Down
7 changes: 6 additions & 1 deletion src/app/qgsrasterlayerproperties.h
Expand Up @@ -119,6 +119,8 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
void histoActionTriggered( QAction* );
/** Draw the min/max markers on the histogram plot. */
void updateHistoMarkers();
/** Button to compute the histogram, appears when no cached histogram is available. */
void on_btnHistoCompute_clicked();

signals:
/** emitted when changes to layer were saved to update legend */
Expand Down Expand Up @@ -182,6 +184,9 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
QwtPlotMarker* mHistoMarkerMax;
double mHistoMin;
double mHistoMax;
QVector<QColor> mHistoColors;
QVector<QColor> mHistoColors;

/** \brief Compute the histogram on demand. */
bool computeHistogram( bool forceComputeFlag );
};
#endif
16 changes: 13 additions & 3 deletions src/core/qgsrasterdataprovider.h
Expand Up @@ -38,7 +38,7 @@ class QgsPoint;
class QByteArray;

#define TINY_VALUE std::numeric_limits<double>::epsilon() * 20

#define RASTER_HISTOGRAM_BINS 256

/** \ingroup core
* Base class for raster data providers.
Expand Down Expand Up @@ -347,15 +347,25 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider
return QStringList();
}

/** \brief test if the requested histogram is already available */

virtual bool hasCachedHistogram( int theBandNoInt, int theBinCountInt = RASTER_HISTOGRAM_BINS )
{
Q_UNUSED( theBandNoInt ); Q_UNUSED( theBinCountInt ); return false;
}

/** \brief Populate the histogram vector for a given band */

virtual void populateHistogram( int theBandNoInt,
QgsRasterBandStats & theBandStats,
int theBinCountInt = 256,
int theBinCountInt = RASTER_HISTOGRAM_BINS,
bool theIgnoreOutOfRangeFlag = true,
bool theThoroughBandScanFlag = false
)
{ Q_UNUSED( theBandNoInt ); Q_UNUSED( theBandStats ); Q_UNUSED( theBinCountInt ); Q_UNUSED( theIgnoreOutOfRangeFlag ); Q_UNUSED( theThoroughBandScanFlag ); }
{
Q_UNUSED( theBandNoInt ); Q_UNUSED( theBandStats ); Q_UNUSED( theBinCountInt );
Q_UNUSED( theIgnoreOutOfRangeFlag ); Q_UNUSED( theThoroughBandScanFlag );
}

/** \brief Create pyramid overviews */
virtual QString buildPyramids( const QList<QgsRasterPyramid> & thePyramidList,
Expand Down
9 changes: 9 additions & 0 deletions src/core/raster/qgsrasterlayer.cpp
Expand Up @@ -1421,6 +1421,15 @@ QPixmap QgsRasterLayer::paletteAsPixmap( int theBandNumber )
}
}

/*
* @param theBandNoInt - which band to compute the histogram for
* @param theBinCountInt - how many 'bins' to categorise the data into
*/
bool QgsRasterLayer::hasCachedHistogram( int theBandNo, int theBinCount )
{
return mDataProvider->hasCachedHistogram( theBandNo, theBinCount );
}

/*
* @param theBandNoInt - which band to compute the histogram for
* @param theBinCountInt - how many 'bins' to categorise the data into
Expand Down
7 changes: 6 additions & 1 deletion src/core/raster/qgsrasterlayer.h
Expand Up @@ -633,10 +633,15 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
const QString & theResamplingMethod = "NEAREST",
bool theTryInternalFlag = false );

/** \brief test if the requested histogram is already available */

bool hasCachedHistogram( int theBandNoInt,
int theBinCountInt = RASTER_HISTOGRAM_BINS );

/** \brief Populate the histogram vector for a given band */

void populateHistogram( int theBandNoInt,
int theBinCountInt = 256,
int theBinCountInt = RASTER_HISTOGRAM_BINS,
bool theIgnoreOutOfRangeFlag = true,
bool theThoroughBandScanFlag = false );

Expand Down
32 changes: 29 additions & 3 deletions src/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -49,7 +49,6 @@
#include "gdalwarper.h"
#include "ogr_spatialref.h"
#include "cpl_conv.h"
#include "cpl_string.h"


static QString PROVIDER_KEY = "gdal";
Expand Down Expand Up @@ -1421,7 +1420,33 @@ QStringList QgsGdalProvider::subLayers( GDALDatasetH dataset )
return subLayers;
}

void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & theBandStats, int theBinCount, bool theIgnoreOutOfRangeFlag, bool theHistogramEstimatedFlag )
bool QgsGdalProvider::hasCachedHistogram( int theBandNo, int theBinCount )
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
if ( ! myGdalBand )
return false;

// get default histo
double myMinVal, myMaxVal;
QgsRasterBandStats theBandStats = bandStatistics( theBandNo );
double myerval = ( theBandStats.maximumValue - theBandStats.minimumValue ) / theBinCount;
myMinVal = theBandStats.minimumValue - 0.1*myerval;
myMaxVal = theBandStats.maximumValue + 0.1*myerval;
int *myHistogramArray=0;
CPLErr myError = GDALGetDefaultHistogram( myGdalBand, &myMinVal, &myMaxVal,
&theBinCount, &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 )
return false;
return true;

}

void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & theBandStats, int theBinCount, bool theIgnoreOutOfRangeFlag, bool theHistogramEstimatedFlag )
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
//QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo );
Expand Down Expand Up @@ -1457,10 +1482,11 @@ void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & t
myProg.type = ProgressHistogram;
myProg.provider = this;
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 custome gdal progress callback
&myProg ); //this is the arg for our custom gdal progress callback

for ( int myBin = 0; myBin < theBinCount; myBin++ )
{
Expand Down
3 changes: 2 additions & 1 deletion src/providers/gdal/qgsgdalprovider.h
Expand Up @@ -244,9 +244,10 @@ class QgsGdalProvider : public QgsRasterDataProvider
*/
QgsRasterBandStats bandStatistics( int theBandNo );

bool hasCachedHistogram( int theBandNoInt, int theBinCountInt = RASTER_HISTOGRAM_BINS );
void populateHistogram( int theBandNoInt,
QgsRasterBandStats & theBandStats,
int theBinCountInt = 256,
int theBinCountInt = RASTER_HISTOGRAM_BINS,
bool theIgnoreOutOfRangeFlag = true,
bool theThoroughBandScanFlag = false
);
Expand Down
61 changes: 60 additions & 1 deletion src/ui/qgsrasterlayerpropertiesbase.ui
Expand Up @@ -932,7 +932,7 @@ p, li { white-space: pre-wrap; }
<item row="0" column="0" colspan="2">
<widget class="QwtPlot" name="mpPlot"/>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QStackedWidget" name="stackedWidget2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
Expand Down Expand Up @@ -1288,8 +1288,67 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<widget class="QWidget" name="page">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_20">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnHistoCompute">
<property name="text">
<string>Compute Histogram</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_19">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
Expand Down

0 comments on commit cf58d3e

Please sign in to comment.