add UI to pick min/max band values in histogram

This commit is contained in:
Etienne Tourigny 2012-06-17 23:22:41 -03:00
parent b70a0c6991
commit 4a53350d7c
12 changed files with 1126 additions and 87 deletions

View File

@ -389,6 +389,7 @@
<file>themes/gis/mActionTouch.png</file>
<file>themes/default/mActionAddMssqlLayer.png</file>
<file>themes/default/mActionTouch.png</file>
<file>themes/default/mActionTouch2.png</file>
<file>themes/classic/mActionTouch.png</file>
<file>flags/af.png</file>
<file>flags/ar.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -68,6 +68,9 @@
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_picker.h>
#include <qwt_picker_machine.h>
QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanvas* theCanvas, QWidget *parent, Qt::WFlags fl )
: QDialog( parent, fl ),
@ -180,14 +183,9 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanv
QSettings settings;
restoreGeometry( settings.value( "/Windows/RasterLayerProperties/geometry" ).toByteArray() );
tabBar->setCurrentIndex( settings.value( "/Windows/RasterLayerProperties/row" ).toInt() );
setWindowTitle( tr( "Layer Properties - %1" ).arg( lyr->name() ) );
int myHistogramTab = 5;
if ( tabBar->currentIndex() == myHistogramTab )
{
refreshHistogram();
}
tableTransparency->horizontalHeader()->setResizeMode( 0, QHeaderView::Stretch );
tableTransparency->horizontalHeader()->setResizeMode( 1, QHeaderView::Stretch );
@ -299,8 +297,87 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanv
}
on_mRenderTypeComboBox_currentIndexChanged( mRenderTypeComboBox->currentIndex() );
// histogram
mHistoPicker = NULL;
mHistoMarkerMin = NULL;
mHistoMarkerMax = NULL;
if ( tabPageHistogram->isEnabled() )
{
//band selector
int myBandCountInt = mRasterLayer->bandCount();
for ( int myIteratorInt = 1;
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
cboHistoBand->addItem( mRasterLayer->bandName( myIteratorInt ) );
}
// histo min/max selectors
leHistoMin->setValidator( new QDoubleValidator( this ) );
leHistoMax->setValidator( new QDoubleValidator( this ) );
// this might generate many refresh events! test..
// connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
// connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
// connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMin() ) );
// connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMax() ) );
connect( leHistoMin, SIGNAL( editingFinished() ), this, SLOT( applyHistoMin() ) );
connect( leHistoMax, SIGNAL( editingFinished() ), this, SLOT( applyHistoMax() ) );
connect( cbxHistoShow, SIGNAL( toggled( bool ) ), this, SLOT( updateHistoMarkers() ) );
// histo actions
QMenu* menu = new QMenu( this );
menu->setSeparatorsCollapsible( false );
btnHistoActions->setMenu( menu );
QActionGroup* group = new QActionGroup( this );
group->setExclusive( false );
connect( group, SIGNAL( triggered( QAction* ) ), this, SLOT( histoActionTriggered( QAction* ) ) );
QAction* action;
// action = new QAction( tr( "Load min/max from band" ), group );
action = new QAction( tr( "Load min/max" ), group );
action->setSeparator( true );
menu->addAction( action );
action = new QAction( tr( "Estimate (faster)" ), group );
action->setData( QVariant( "Load estimate" ) );
menu->addAction( action );
action = new QAction( tr( "Actual (slower)" ), group );
action->setData( QVariant( "Load actual" ) );
menu->addAction( action );
action = new QAction( tr( "Current extent" ), group );
action->setData( QVariant( "Load extent" ) );
menu->addAction( action );
// stddev was removed...
action = new QAction( tr( "Use 1 stddev" ), group );
action->setData( QVariant( "Load 1 stddev" ) );
menu->addAction( action );
/*
action = new QAction( tr( "Use custom stddev" ), group );
action->setData( QVariant( "Load stddev" ) );
menu->addAction( action );
*/
action = new QAction( tr( "Reset" ), group );
action->setData( QVariant( "Load reset" ) );
menu->addAction( action );
action = new QAction( tr( "Load for all bands" ), group );
action->setData( QVariant( "Load apply all" ) );
action->setCheckable( true );
action->setChecked( true );
menu->addAction( action );
}
// update based on lyr's current state
sync();
// set current tab after everything has been initialized
tabBar->setCurrentIndex( settings.value( "/Windows/RasterLayerProperties/row" ).toInt() );
// show histogram tab
int myHistogramTab = 5;
if ( tabBar->currentIndex() == myHistogramTab )
{
refreshHistogram();
}
} // QgsRasterLayerProperties ctor
@ -1106,6 +1183,13 @@ void QgsRasterLayerProperties::on_tabBar_currentChanged( int theTab )
{
refreshHistogram();
}
else
{
if ( QApplication::overrideCursor() )
QApplication::restoreOverrideCursor();
btnHistoMin->setChecked( false );
btnHistoMax->setChecked( false );
}
}
void QgsRasterLayerProperties::refreshHistogram()
@ -1113,7 +1197,8 @@ void QgsRasterLayerProperties::refreshHistogram()
#if !defined(QWT_VERSION) || QWT_VERSION<0x060000
mpPlot->clear();
#endif
mHistogramProgress->show();
// mHistogramProgress->show();
stackedWidget2->setCurrentIndex( 1 );
connect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
QApplication::setOverrideCursor( Qt::WaitCursor );
QgsDebugMsg( "entered." );
@ -1135,7 +1220,7 @@ void QgsRasterLayerProperties::refreshHistogram()
// 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 pixesl in any given
// 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
//
@ -1143,9 +1228,13 @@ void QgsRasterLayerProperties::refreshHistogram()
bool myIgnoreOutOfRangeFlag = true;
bool myThoroughBandScanFlag = false;
int myBandCountInt = mRasterLayer->bandCount();
QList<QColor> myColors;
myColors << Qt::black << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkRed << Qt::darkGreen << Qt::darkBlue;
// make colors list
mHistoColors.clear();
mHistoColors << Qt::black; // first element, not used
// get a list fo colors
QVector<QColor> myColors;
myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
while ( myColors.size() <= myBandCountInt )
{
myColors <<
@ -1154,6 +1243,70 @@ void QgsRasterLayerProperties::refreshHistogram()
1 + ( int )( 255.0 * rand() / ( RAND_MAX + 1.0 ) ) );
}
// assign colors to each band, depending on the current RGB/gray band selection
// TODO paletted + pseudo ?
QString rendererName = mRenderTypeComboBox->itemData( mRenderTypeComboBox->currentIndex() ).toString();
// greyscale
if ( rendererName == "singlebandgray" )
{
int myGrayBand = mRendererWidget->selectedBand();
for ( int i = 1; i <= myBandCountInt; i++ )
{
if ( i == myGrayBand )
mHistoColors << Qt::black;
else
{
if ( ! myColors.isEmpty() )
{
mHistoColors << myColors.first();
myColors.pop_front();
}
else
{
mHistoColors << Qt::black;
}
}
}
}
// RGB
else if ( rendererName == "multibandcolor" )
{
int myRedBand = mRendererWidget->selectedBand( 0 );
int myGreenBand = mRendererWidget->selectedBand( 1 );
int myBlueBand = mRendererWidget->selectedBand( 2 );
// remove RGB, which are reserved for the actual RGB bands
// show name of RGB bands in appropriate color in bold
myColors.remove( 0, 3 );
for ( int i = 1; i <= myBandCountInt; i++ )
{
QColor myColor;
if ( i == myRedBand )
myColor = Qt::red;
else if ( i == myGreenBand )
myColor = Qt::green;
else if ( i == myBlueBand )
myColor = Qt::blue;
else
{
if ( ! myColors.isEmpty() )
{
myColor = myColors.first();
myColors.pop_front();
}
else
{
myColor = Qt::black;
}
cboHistoBand->setItemData( i - 1, Qt::black, Qt::ForegroundRole );
}
if ( i == myRedBand || i == myGreenBand || i == myBlueBand )
{
cboHistoBand->setItemData( i - 1, myColor, Qt::ForegroundRole );
}
mHistoColors << myColor;
}
}
//
//now draw actual graphs
//
@ -1164,8 +1317,8 @@ void QgsRasterLayerProperties::refreshHistogram()
//
// scan through to get counts from layers' histograms
//
float myGlobalMin = 0;
float myGlobalMax = 0;
mHistoMin = 0;
mHistoMax = 0;
bool myFirstIteration = true;
for ( int myIteratorInt = 1;
myIteratorInt <= myBandCountInt;
@ -1176,7 +1329,7 @@ void QgsRasterLayerProperties::refreshHistogram()
QwtPlotCurve * mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
mypCurve->setPen( QPen( myColors.at( myIteratorInt ) ) );
mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
#if defined(QWT_VERSION) && QWT_VERSION>=0x060000
QVector<QPointF> data;
#else
@ -1199,26 +1352,51 @@ void QgsRasterLayerProperties::refreshHistogram()
mypCurve->setData( myX2Data, myY2Data );
#endif
mypCurve->attach( mpPlot );
if ( myFirstIteration || myGlobalMin < myRasterBandStats.minimumValue )
if ( myFirstIteration || mHistoMin > myRasterBandStats.minimumValue )
{
myGlobalMin = myRasterBandStats.minimumValue;
mHistoMin = myRasterBandStats.minimumValue;
}
if ( myFirstIteration || myGlobalMax < myRasterBandStats.maximumValue )
if ( myFirstIteration || mHistoMax < myRasterBandStats.maximumValue )
{
myGlobalMax = myRasterBandStats.maximumValue;
mHistoMax = myRasterBandStats.maximumValue;
}
QgsDebugMsg( QString( "computed histo min = %1 max = %2" ).arg( mHistoMin ).arg( mHistoMax ) );
myFirstIteration = false;
}
// for x axis use band pixel values rather than gdal hist. bin values
// subtract -0.5 to prevent rounding errors
// see http://www.gdal.org/classGDALRasterBand.html#3f8889607d3b2294f7e0f11181c201c8
mpPlot->setAxisScale( QwtPlot::xBottom,
myGlobalMin - 0.5,
myGlobalMax + 0.5 );
mHistoMin - 0.5,
mHistoMax + 0.5 );
mpPlot->replot();
// histo plot markers
// memory leak?
mHistoMarkerMin = new QwtPlotMarker();
mHistoMarkerMin->attach( mpPlot );
mHistoMarkerMax = new QwtPlotMarker();
mHistoMarkerMax->attach( mpPlot );
updateHistoMarkers();
// histo picker
if ( ! mHistoPicker )
{
mHistoPicker = new QwtPlotPicker( mpPlot->canvas() );
mHistoPicker->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::DragSelection );
// mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
mHistoPicker->setTrackerMode( QwtPicker::AlwaysOff );
mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
mHistoPicker->setEnabled( false );
connect( mHistoPicker, SIGNAL( selected( const QwtDoublePoint & ) ), this, SLOT( histoPickerSelected( const QwtDoublePoint & ) ) );
}
disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
mHistogramProgress->hide();
// mHistogramProgress->hide();
stackedWidget2->setCurrentIndex( 0 );
mpPlot->canvas()->setCursor( Qt::ArrowCursor );
on_cboHistoBand_currentIndexChanged( -1 );
QApplication::restoreOverrideCursor();
}
@ -1590,3 +1768,334 @@ void QgsRasterLayerProperties::toggleBuildPyramidsButton()
}
}
void QgsRasterLayerProperties::on_cboHistoBand_currentIndexChanged( int index )
{
// get the current index value, index can be -1
index = cboHistoBand->currentIndex();
if ( mHistoPicker != NULL )
{
mHistoPicker->setEnabled( false );
mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
}
btnHistoMin->setEnabled( true );
btnHistoMax->setEnabled( true );
int theBandNo = index + 1;
QString minStr, maxStr;
// TODO - there are 2 definitions of raster data type that should be unified
// QgsRasterDataProvider::DataType and QgsContrastEnhancement::QgsRasterDataType
// TODO - fix gdal provider: changes data type when nodata value is not found
// this prevents us from getting proper min and max values here
// minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( QgsContrastEnhancement::QgsRasterDataType )
// mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
// maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( QgsContrastEnhancement::QgsRasterDataType )
// mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
QString rendererName = mRenderTypeComboBox->itemData( mRenderTypeComboBox->currentIndex() ).toString();
// TODO paletted + pseudo ?
if ( rendererName == "singlebandgray" )
{
if ( theBandNo == mRendererWidget->selectedBand() )
{
minStr = mRendererWidget->min();
maxStr = mRendererWidget->max();
}
}
else if ( rendererName == "multibandcolor" )
{
for ( int i = 0; i <= 2; i++ )
{
if ( theBandNo == mRendererWidget->selectedBand( i ) )
{
minStr = mRendererWidget->min( i );
maxStr = mRendererWidget->max( i );
}
}
}
leHistoMin->setText( minStr );
leHistoMax->setText( maxStr );
applyHistoMin();
applyHistoMax();
}
void QgsRasterLayerProperties::histoActionTriggered( QAction* action )
{
if ( ! action )
return;
// this approach is a bit of a hack, but we don't have to define slots for each action
QString actionName = action->data().toString();
QgsDebugMsg( QString( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
if ( actionName.left( 5 ) == "Load " )
{
if ( actionName == "Load apply all" )
return;
QgsRasterBandStats myRasterBandStats;
QVector<int> myBands;
double minMaxValues[2];
bool ok = false;
// get "Load apply all" value to find which band(s) need updating
QList< QAction* > actions;
if ( action->actionGroup() )
actions = action->actionGroup()->actions();
foreach( QAction* tmpAction, actions )
{
if ( tmpAction && ( tmpAction->data().toString() == "Load apply all" ) )
{
// add all bands
if ( tmpAction->isChecked() )
{
int myBandCountInt = mRasterLayer->bandCount();
for ( int myIteratorInt = 1;
myIteratorInt <= myBandCountInt;
++myIteratorInt )
{
if ( myIteratorInt != cboHistoBand->currentIndex() + 1 )
myBands << myIteratorInt;
}
}
// add current band to the end
myBands << cboHistoBand->currentIndex() + 1;
break;
}
}
// don't update markers every time
if ( myBands.size() > 1 )
{
leHistoMin->blockSignals( true );
leHistoMax->blockSignals( true );
}
foreach( int theBandNo, myBands )
{
ok = false;
if ( actionName == "Load actual" )
{
ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
theBandNo, minMaxValues );
}
else if ( actionName == "Load estimate" )
{
ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
theBandNo, minMaxValues );
}
else if ( actionName == "Load extent" )
{
ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
theBandNo, minMaxValues );
}
else if ( actionName == "Load 1 stddev" )
{
double myStdDev = 1.0;
ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, theBandNo, minMaxValues );
}
// apply current item
cboHistoBand->setCurrentIndex( theBandNo - 1 );
if ( !ok || actionName == "Load reset" )
{
leHistoMin->clear();
leHistoMax->clear();
// TODO - fix gdal provider: changes data type when nodata value is not found
// this prevents us from getting proper min and max values here
// minMaxValues[0] = QgsContrastEnhancement::minimumValuePossible( ( QgsContrastEnhancement::QgsRasterDataType )
// mRasterLayer->dataProvider()->dataType( theBandNo ) );
// minMaxValues[1] = QgsContrastEnhancement::maximumValuePossible( ( QgsContrastEnhancement::QgsRasterDataType )
// mRasterLayer->dataProvider()->dataType( theBandNo ) );
}
else
{
leHistoMin->setText( QString::number( minMaxValues[0] ) );
leHistoMax->setText( QString::number( minMaxValues[1] ) );
}
applyHistoMin( );
applyHistoMax( );
}
// update markers
if ( myBands.size() > 1 )
{
leHistoMin->blockSignals( false );
leHistoMax->blockSignals( false );
updateHistoMarkers();
}
}
else
{
return;
}
}
void QgsRasterLayerProperties::applyHistoMin( )
{
int theBandNo = cboHistoBand->currentIndex() + 1;
QString rendererName = mRenderTypeComboBox->itemData( mRenderTypeComboBox->currentIndex() ).toString();
// TMP ET TODO - paletted + pseudo
if ( rendererName.startsWith( "singlebandgray" ) )
{
if ( theBandNo == mRendererWidget->selectedBand( ) )
{
mRendererWidget->setMin( leHistoMin->text() );
}
}
else if ( rendererName == "multibandcolor" )
{
for ( int i = 0; i <= 2; i++ )
{
if ( theBandNo == mRendererWidget->selectedBand( i ) )
mRendererWidget->setMin( leHistoMin->text(), i );
}
}
updateHistoMarkers();
}
void QgsRasterLayerProperties::applyHistoMax( )
{
int theBandNo = cboHistoBand->currentIndex() + 1;
QString rendererName = mRenderTypeComboBox->itemData( mRenderTypeComboBox->currentIndex() ).toString();
if ( rendererName.startsWith( "singleband" ) )
{
if ( theBandNo == mRendererWidget->selectedBand( ) )
{
mRendererWidget->setMax( leHistoMax->text() );
}
}
else if ( rendererName == "multibandcolor" )
{
for ( int i = 0; i <= 2; i++ )
{
if ( theBandNo == mRendererWidget->selectedBand( i ) )
mRendererWidget->setMax( leHistoMax->text(), i );
}
}
updateHistoMarkers();
}
void QgsRasterLayerProperties::on_btnHistoMin_toggled()
{
if ( mpPlot != NULL && mHistoPicker != NULL )
{
if ( QApplication::overrideCursor() )
QApplication::restoreOverrideCursor();
if ( btnHistoMin->isChecked() )
{
btnHistoMax->setChecked( false );
QApplication::setOverrideCursor( Qt::PointingHandCursor );
}
mHistoPicker->setEnabled( btnHistoMin->isChecked() );
}
}
void QgsRasterLayerProperties::on_btnHistoMax_toggled()
{
if ( mpPlot != NULL && mHistoPicker != NULL )
{
if ( QApplication::overrideCursor() )
QApplication::restoreOverrideCursor();
if ( btnHistoMax->isChecked() )
{
btnHistoMin->setChecked( false );
QApplication::setOverrideCursor( Qt::PointingHandCursor );
}
mHistoPicker->setEnabled( btnHistoMax->isChecked() );
}
}
// local function used by histoPickerSelected(), to get a rounded picked value
// this is sensitive and may not always be correct, needs more testing
QString findClosestTickVal( double target, QwtScaleDiv * scale, int div = 100 )
{
if ( scale == NULL ) return "";
QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
double diff = ( minorTicks[1] - minorTicks[0] ) / div;
double min = majorTicks[0] - diff;
if ( min > target )
min -= ( majorTicks[1] - majorTicks[0] );
double max = scale->upperBound();
double closest = target;
double current = min;
while ( current < max )
{
current += diff;
if ( current > target )
{
closest = ( abs( target - current + diff ) < abs( target - current ) ) ? current - diff : current;
break;
}
}
QgsDebugMsg( QString( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
return QString::number( closest );
}
void QgsRasterLayerProperties::histoPickerSelected( const QwtDoublePoint & pos )
{
if ( btnHistoMin->isChecked() )
{
leHistoMin->setText( findClosestTickVal( pos.x(), mpPlot->axisScaleDiv( QwtPlot::xBottom ) ) );
applyHistoMin();
btnHistoMin->setChecked( false );
}
else if ( btnHistoMax->isChecked() )
{
leHistoMax->setText( findClosestTickVal( pos.x(), mpPlot->axisScaleDiv( QwtPlot::xBottom ) ) );
applyHistoMax();
btnHistoMax->setChecked( false );
}
if ( QApplication::overrideCursor() )
QApplication::restoreOverrideCursor();
}
void QgsRasterLayerProperties::updateHistoMarkers( )
{
// hack to not update markers
if ( leHistoMin->signalsBlocked() )
return;
// todo error checking
if ( mpPlot == NULL || mHistoMarkerMin == NULL || mHistoMarkerMax == NULL )
return;
if ( ! cbxHistoShow->isChecked() )
{
mHistoMarkerMin->hide();
mHistoMarkerMax->hide();
mpPlot->replot();
return;
}
int theBandNo = cboHistoBand->currentIndex() + 1;
double minVal = mHistoMin;
double maxVal = mHistoMax;
QString minStr = leHistoMin->text();
QString maxStr = leHistoMax->text();
if ( minStr != "" )
minVal = minStr.toDouble();
if ( maxStr != "" )
maxVal = maxStr.toDouble();
QPen linePen = QPen( mHistoColors.at( theBandNo ) );
linePen.setStyle( Qt::DashLine );
mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
mHistoMarkerMin->setLinePen( linePen );
mHistoMarkerMin->setXValue( minVal );
mHistoMarkerMin->show();
mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
mHistoMarkerMax->setLinePen( linePen );
mHistoMarkerMax->setXValue( maxVal );
mHistoMarkerMax->show();
mpPlot->replot();
}

View File

@ -31,6 +31,8 @@ class QgsRasterLayer;
class QgsMapToolEmitPoint;
class QgsRasterRenderer;
class QgsRasterRendererWidget;
class QwtPlotPicker;
class QwtPlotMarker;
/**Property sheet for a raster map layer
*@author Tim Sutton
@ -100,6 +102,23 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
/**Enable or disable Build pyramids button depending on selection in pyramids list*/
void toggleBuildPyramidsButton();
// histogram
/** Used when the histogram band selector changes, or when tab is loaded. */
void on_cboHistoBand_currentIndexChanged( int );
/** Applies the selected min/max values to the renderer widget. */
void applyHistoMin( );
void applyHistoMax( );
/** Button to activate picking of the min/max value on the graph. */
void on_btnHistoMin_toggled();
void on_btnHistoMax_toggled();
/** Called when a selection has been made using the plot picker. */
void histoPickerSelected( const QwtDoublePoint & );
/** Various actions that are stored in btnHistoActions. */
void histoActionTriggered( QAction* );
/** Draw the min/max markers on the histogram plot. */
void updateHistoMarkers();
signals:
/** emitted when changes to layer were saved to update legend */
void refreshLegend( QString layerID, bool expandItem );
@ -154,5 +173,13 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
QgsMapCanvas* mMapCanvas;
QgsMapToolEmitPoint* mPixelSelectorTool;
// histogram
QwtPlotPicker* mHistoPicker;
QwtPlotMarker* mHistoMarkerMin;
QwtPlotMarker* mHistoMarkerMax;
double mHistoMin;
double mHistoMax;
QVector<QColor> mHistoColors;
};
#endif

View File

@ -204,38 +204,20 @@ void QgsMultiBandColorRendererWidget::loadMinMaxValueForBand( int band, QLineEdi
return;
}
QgsRasterDataProvider* provider = mRasterLayer->dataProvider();
if ( !provider )
{
return;
}
double minMaxValues[2];
bool ok = false;
if ( band < 0 )
{
minEdit->clear();
maxEdit->clear();
return;
}
double minVal = 0;
double maxVal = 0;
if ( mEstimateRadioButton->isChecked() )
{
minVal = provider->minimumValue( band );
maxVal = provider->maximumValue( band );
ok = bandMinMax( Estimate, band, minMaxValues );
}
else if ( mActualRadioButton->isChecked() )
{
QgsRasterBandStats rasterBandStats = mRasterLayer->bandStatistics( band );
minVal = rasterBandStats.minimumValue;
maxVal = rasterBandStats.maximumValue;
ok = bandMinMax( Actual, band, minMaxValues );
}
else if ( mCurrentExtentRadioButton->isChecked() )
{
double minMax[2];
mRasterLayer->computeMinimumMaximumFromLastExtent( band, minMax );
minVal = minMax[0];
maxVal = minMax[1];
ok = bandMinMax( CurrentExtent, band, minMaxValues );
}
else if ( mUseStdDevRadioButton->isChecked() )
{
@ -245,8 +227,16 @@ void QgsMultiBandColorRendererWidget::loadMinMaxValueForBand( int band, QLineEdi
maxVal = rasterBandStats.mean + diff;
}
minEdit->setText( QString::number( minVal ) );
maxEdit->setText( QString::number( maxVal ) );
if ( ok )
{
minEdit->setText( QString::number( minMaxValues[0] ) );
maxEdit->setText( QString::number( minMaxValues[1] ) );
}
else
{
minEdit->clear();
maxEdit->clear();
}
}
void QgsMultiBandColorRendererWidget::setFromRenderer( const QgsRasterRenderer* r )
@ -269,3 +259,96 @@ void QgsMultiBandColorRendererWidget::setFromRenderer( const QgsRasterRenderer*
mBlueBandComboBox->setCurrentIndex( mBlueBandComboBox->findText( tr( "Blue" ) ) );
}
}
QString QgsMultiBandColorRendererWidget::min( int index )
{
switch ( index )
{
case 0:
return mRedMinLineEdit->text();
break;
case 1:
return mGreenMinLineEdit->text();
break;
case 2:
return mBlueMinLineEdit->text();
break;
default:
break;
}
return QString( );
}
QString QgsMultiBandColorRendererWidget::max( int index )
{
switch ( index )
{
case 0:
return mRedMaxLineEdit->text();
break;
case 1:
return mGreenMaxLineEdit->text();
break;
case 2:
return mBlueMaxLineEdit->text();
break;
default:
break;
}
return QString( );
}
void QgsMultiBandColorRendererWidget::setMin( QString value, int index )
{
switch ( index )
{
case 0:
mRedMinLineEdit->setText( value );
break;
case 1:
mGreenMinLineEdit->setText( value );
break;
case 2:
mBlueMinLineEdit->setText( value );
break;
default:
break;
}
}
void QgsMultiBandColorRendererWidget::setMax( QString value, int index )
{
switch ( index )
{
case 0:
mRedMaxLineEdit->setText( value );
break;
case 1:
mGreenMaxLineEdit->setText( value );
break;
case 2:
mBlueMaxLineEdit->setText( value );
break;
default:
break;
}
}
int QgsMultiBandColorRendererWidget::selectedBand( int index )
{
switch ( index )
{
case 0:
return mRedBandComboBox->currentIndex();
break;
case 1:
return mGreenBandComboBox->currentIndex();
break;
case 2:
return mBlueBandComboBox->currentIndex();
break;
default:
break;
}
return -1;
}

View File

@ -40,6 +40,12 @@ class GUI_EXPORT QgsMultiBandColorRendererWidget: public QgsRasterRendererWidget
void setFromRenderer( const QgsRasterRenderer* r );
QString min( int index = 0 );
QString max( int index = 0 );
void setMin( QString value, int index = 0 );
void setMax( QString value, int index = 0 );
int selectedBand( int index = 0 );
private slots:
void on_mLoadPushButton_clicked();

View File

@ -41,3 +41,64 @@ QString QgsRasterRendererWidget::displayBandName( int band ) const
}
return name;
}
bool QgsRasterRendererWidget::bandMinMax( LoadMinMaxAlgo loadAlgo, int band, double* minMaxValues )
{
if ( !mRasterLayer )
{
return false;
}
QgsRasterDataProvider* provider = mRasterLayer->dataProvider();
if ( !provider )
{
return false;
}
if ( band < 0 )
{
return false;
}
if ( loadAlgo == Estimate )
{
minMaxValues[0] = provider->minimumValue( band );
minMaxValues[1] = provider->maximumValue( band );
}
else if ( loadAlgo == Actual )
{
QgsRasterBandStats rasterBandStats = mRasterLayer->bandStatistics( band );
minMaxValues[0] = rasterBandStats.minimumValue;
minMaxValues[1] = rasterBandStats.maximumValue;
}
else if ( loadAlgo == CurrentExtent )
{
mRasterLayer->computeMinimumMaximumFromLastExtent( band, minMaxValues );
}
else
{
return false;
}
return true;
}
bool QgsRasterRendererWidget::bandMinMaxFromStdDev( double stdDev, int band, double* minMaxValues )
{
if ( !mRasterLayer )
{
return false;
}
QgsRasterDataProvider* provider = mRasterLayer->dataProvider();
if ( !provider )
{
return false;
}
if ( band < 0 )
{
return false;
}
QgsRasterBandStats myRasterBandStats = mRasterLayer->bandStatistics( band );
minMaxValues[0] = myRasterBandStats.mean - ( stdDev * myRasterBandStats.stdDev );
minMaxValues[1] = myRasterBandStats.mean + ( stdDev * myRasterBandStats.stdDev );
return true;
}

View File

@ -29,11 +29,27 @@ class GUI_EXPORT QgsRasterRendererWidget: public QWidget
QgsRasterRendererWidget( QgsRasterLayer* layer ) { mRasterLayer = layer; }
virtual ~QgsRasterRendererWidget() {}
enum LoadMinMaxAlgo
{
Estimate,
Actual,
CurrentExtent
};
virtual QgsRasterRenderer* renderer() = 0;
void setRasterLayer( QgsRasterLayer* layer ) { mRasterLayer = layer; }
const QgsRasterLayer* rasterLayer() const { return mRasterLayer; }
virtual QString min( int index = 0 ) { Q_UNUSED( index ); return QString( ); }
virtual QString max( int index = 0 ) { Q_UNUSED( index ); return QString( ); }
virtual void setMin( QString value, int index = 0 ) { Q_UNUSED( index ); Q_UNUSED( value ); }
virtual void setMax( QString value, int index = 0 ) { Q_UNUSED( index ); Q_UNUSED( value ); }
virtual int selectedBand( int index = 0 ) { Q_UNUSED( index ); return -1; }
bool bandMinMax( LoadMinMaxAlgo loadAlgo, int band, double *values );
bool bandMinMaxFromStdDev( double stdDev, int band, double *values );
protected:
QgsRasterLayer* mRasterLayer;
/**Returns a band name for display. First choice is color name, otherwise band number*/

View File

@ -82,36 +82,27 @@ QgsRasterRenderer* QgsSingleBandGrayRendererWidget::renderer()
void QgsSingleBandGrayRendererWidget::on_mLoadPushButton_clicked()
{
if ( !mRasterLayer )
{
return;
}
QgsRasterDataProvider* provider = mRasterLayer->dataProvider();
if ( !provider )
{
return;
}
int band = mGrayBandComboBox->itemData( mGrayBandComboBox->currentIndex() ).toInt();
double minVal = 0;
double maxVal = 0;
double minMaxValues[2];
bool ok = false;
if ( mEstimateRadioButton->isChecked() )
{
minVal = provider->minimumValue( band );
maxVal = provider->maximumValue( band );
ok = bandMinMax( Estimate, band, minMaxValues );
}
else if ( mActualRadioButton->isChecked() )
{
QgsRasterBandStats rasterBandStats = mRasterLayer->bandStatistics( band );
minVal = rasterBandStats.minimumValue;
maxVal = rasterBandStats.maximumValue;
ok = bandMinMax( Actual, band, minMaxValues );
}
else if ( mCurrentExtentRadioButton->isChecked() )
{
double minMax[2];
mRasterLayer->computeMinimumMaximumFromLastExtent( band, minMax );
minVal = minMax[0];
maxVal = minMax[1];
ok = bandMinMax( CurrentExtent, band, minMaxValues );
}
if ( ok )
{
mMinLineEdit->setText( QString::number( minMaxValues[0] ) );
mMaxLineEdit->setText( QString::number( minMaxValues[1] ) );
}
else if ( mUseStdDevRadioButton->isChecked() )
{
@ -122,11 +113,9 @@ void QgsSingleBandGrayRendererWidget::on_mLoadPushButton_clicked()
}
else
{
return;
mMinLineEdit->clear();
mMaxLineEdit->clear();
}
mMinLineEdit->setText( QString::number( minVal ) );
mMaxLineEdit->setText( QString::number( maxVal ) );
}
void QgsSingleBandGrayRendererWidget::setFromRenderer( const QgsRasterRenderer* r )

View File

@ -34,6 +34,12 @@ class GUI_EXPORT QgsSingleBandGrayRendererWidget: public QgsRasterRendererWidget
void setFromRenderer( const QgsRasterRenderer* r );
QString min( int index = 0 ) { Q_UNUSED( index ); return mMinLineEdit->text(); }
QString max( int index = 0 ) { Q_UNUSED( index ); return mMaxLineEdit->text(); }
void setMin( QString value, int index = 0 ) { Q_UNUSED( index ); mMinLineEdit->setText( value ); }
void setMax( QString value, int index = 0 ) { Q_UNUSED( index ); mMaxLineEdit->setText( value ); }
int selectedBand( int index = 0 ) { Q_UNUSED( index ); return mGrayBandComboBox->currentIndex() + 1; }
private slots:
void on_mLoadPushButton_clicked();
};

View File

@ -1467,12 +1467,12 @@ void QgsGdalProvider::populateHistogram( int theBandNo, QgsRasterBandStats & t
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!" );
// 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" );
// QgsDebugMsg( "Added " + QString::number( myHistogramArray[myBin] ) + " to histogram vector" );
}
}

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<width>722</width>
<height>663</height>
</rect>
</property>
@ -825,7 +825,7 @@
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;table border=&quot;0&quot; style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
&lt;tr&gt;
&lt;td style=&quot;border: none;&quot;&gt;
@ -933,21 +933,361 @@ p, li { white-space: pre-wrap; }
<widget class="QwtPlot" name="mpPlot"/>
</item>
<item row="1" column="0">
<widget class="QProgressBar" name="mHistogramProgress">
<property name="value">
<widget class="QStackedWidget" name="stackedWidget2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="mSaveAsImageButton">
<property name="text">
<string>Save as image...</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionFileSave.png</normaloff>../../images/themes/default/mActionFileSave.png</iconset>
</property>
<widget class="QWidget" name="page1_2">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="spacing">
<number>5</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QToolButton" name="btnHistoActions">
<property name="toolTip">
<string>Load min/max values from Bands</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mAction.png</normaloff>:/images/themes/default/mAction.png</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxHistoShow">
<property name="toolTip">
<string>Show Min and Max on plot</string>
</property>
<property name="text">
<string>Show Min/Max</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_6">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Band:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboHistoBand">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="spacing">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Min:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnHistoMin">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Pick Min value on graph</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionTouch2.png</normaloff>:/images/themes/default/mActionTouch2.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leHistoMin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="spacing">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Max:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnHistoMax">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Pick Max value on graph</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionTouch2.png</normaloff>:/images/themes/default/mActionTouch2.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leHistoMax">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_17">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="mSaveAsImageButton">
<property name="toolTip">
<string>Save plot</string>
</property>
<property name="text">
<string>Save as image...</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFileSave.png</normaloff>:/images/themes/default/mActionFileSave.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page2_2">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="mHistogramProgress">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -1000,6 +1340,7 @@ p, li { white-space: pre-wrap; }
<class>QwtPlot</class>
<extends>QFrame</extends>
<header>qwt_plot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>