QGIS API Documentation  2.99.0-Master (23ddace)
qgsrasterhistogramwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterhistogramwidget.cpp
3  ---------------------------
4  begin : July 2012
5  copyright : (C) 2012 by Etienne Tourigny
6  email : etourigny dot dev at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsapplication.h"
19 #include "qgsguiutils.h"
23 #include "qgsrasterminmaxwidget.h"
24 #include "qgsrasterdataprovider.h"
25 #include "qgssettings.h"
26 
27 #include <QMenu>
28 #include <QFileInfo>
29 #include <QDir>
30 #include <QPainter>
31 
32 // QWT Charting widget
33 #include <qwt_global.h>
34 #include <qwt_plot_canvas.h>
35 #include <qwt_legend.h>
36 #include <qwt_plot.h>
37 #include <qwt_plot_curve.h>
38 #include <qwt_plot_grid.h>
39 #include <qwt_plot_marker.h>
40 #include <qwt_plot_picker.h>
41 #include <qwt_picker_machine.h>
42 #include <qwt_plot_zoomer.h>
43 #include <qwt_plot_layout.h>
44 #include <qwt_plot_renderer.h>
45 #include <qwt_plot_histogram.h>
46 
47 #ifdef Q_OS_WIN
48 #include <time.h>
49 #endif
50 
51 // this has been removed, now we let the provider/raster interface decide
52 // how many bins are suitable depending on data type and range
53 //#define RASTER_HISTOGRAM_BINS 256
54 
56  : QgsMapLayerConfigWidget( lyr, nullptr, parent )
57  , mRasterLayer( lyr )
58  , mRendererWidget( nullptr )
59 {
60  setupUi( this );
61 
62  mSaveAsImageButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
63 
64  mRendererWidget = nullptr;
65  mRendererName = QStringLiteral( "singlebandgray" );
66 
67  mHistoMin = 0;
68  mHistoMax = 0;
69 
70  mHistoPicker = nullptr;
71  mHistoZoomer = nullptr;
72  mHistoMarkerMin = nullptr;
73  mHistoMarkerMax = nullptr;
74 
75  QgsSettings settings;
76  mHistoShowMarkers = settings.value( QStringLiteral( "Raster/histogram/showMarkers" ), false ).toBool();
77  // mHistoLoadApplyAll = settings.value( "/Raster/histogram/loadApplyAll", false ).toBool();
78  mHistoZoomToMinMax = settings.value( QStringLiteral( "Raster/histogram/zoomToMinMax" ), false ).toBool();
79  mHistoUpdateStyleToMinMax = settings.value( QStringLiteral( "Raster/histogram/updateStyleToMinMax" ), true ).toBool();
80  mHistoDrawLines = settings.value( QStringLiteral( "Raster/histogram/drawLines" ), true ).toBool();
81  // mHistoShowBands = (HistoShowBands) settings.value( "/Raster/histogram/showBands", (int) ShowAll ).toInt();
82  mHistoShowBands = ShowAll;
83 
84  bool isInt = true;
85  if ( true )
86  {
87  //band selector
88  int myBandCountInt = mRasterLayer->bandCount();
89  for ( int myIteratorInt = 1;
90  myIteratorInt <= myBandCountInt;
91  ++myIteratorInt )
92  {
93  cboHistoBand->addItem( mRasterLayer->bandName( myIteratorInt ) );
94  Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( myIteratorInt );
95  if ( !( mySrcDataType == Qgis::Byte ||
96  mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
97  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 ) )
98  isInt = false;
99  }
100 
101  // histo min/max selectors
102  leHistoMin->setValidator( new QDoubleValidator( this ) );
103  leHistoMax->setValidator( new QDoubleValidator( this ) );
104  // this might generate many refresh events! test..
105  // connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
106  // connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
107  // connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMin() ) );
108  // connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMax() ) );
109  connect( leHistoMin, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMin );
110  connect( leHistoMax, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMax );
111 
112  // histo actions
113  // TODO move/add options to qgis options dialog
114  QMenu *menu = new QMenu( this );
115  menu->setSeparatorsCollapsible( false );
116  btnHistoActions->setMenu( menu );
117  QActionGroup *group = nullptr;
118  QAction *action = nullptr;
119 
120  // min/max options
121  group = new QActionGroup( this );
122  group->setExclusive( false );
123  connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
124  action = new QAction( tr( "Min/Max options" ), group );
125  action->setSeparator( true );
126  menu->addAction( action );
127  action = new QAction( tr( "Always show min/max markers" ), group );
128  action->setData( QVariant( "Show markers" ) );
129  action->setCheckable( true );
130  action->setChecked( mHistoShowMarkers );
131  menu->addAction( action );
132  action = new QAction( tr( "Zoom to min/max" ), group );
133  action->setData( QVariant( "Zoom min_max" ) );
134  action->setCheckable( true );
135  action->setChecked( mHistoZoomToMinMax );
136  menu->addAction( action );
137  action = new QAction( tr( "Update style to min/max" ), group );
138  action->setData( QVariant( "Update min_max" ) );
139  action->setCheckable( true );
140  action->setChecked( mHistoUpdateStyleToMinMax );
141  menu->addAction( action );
142 
143  // visibility options
144  group = new QActionGroup( this );
145  group->setExclusive( false );
146  connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
147  action = new QAction( tr( "Visibility" ), group );
148  action->setSeparator( true );
149  menu->addAction( action );
150  group = new QActionGroup( this );
151  group->setExclusive( true ); // these options are exclusive
152  connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
153  action = new QAction( tr( "Show all bands" ), group );
154  action->setData( QVariant( "Show all" ) );
155  action->setCheckable( true );
156  action->setChecked( mHistoShowBands == ShowAll );
157  menu->addAction( action );
158  action = new QAction( tr( "Show RGB/Gray band(s)" ), group );
159  action->setData( QVariant( "Show RGB" ) );
160  action->setCheckable( true );
161  action->setChecked( mHistoShowBands == ShowRGB );
162  menu->addAction( action );
163  action = new QAction( tr( "Show selected band" ), group );
164  action->setData( QVariant( "Show selected" ) );
165  action->setCheckable( true );
166  action->setChecked( mHistoShowBands == ShowSelected );
167  menu->addAction( action );
168 
169  // display options
170  group = new QActionGroup( this );
171  group->setExclusive( false );
172  connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
173  action = new QAction( tr( "Display" ), group );
174  action->setSeparator( true );
175  menu->addAction( action );
176  // should we plot as histogram instead of line plot? (int data only)
177  action = new QAction( QLatin1String( "" ), group );
178  action->setData( QVariant( "Draw lines" ) );
179  if ( isInt )
180  {
181  action->setText( tr( "Draw as lines" ) );
182  action->setCheckable( true );
183  action->setChecked( mHistoDrawLines );
184  }
185  else
186  {
187  action->setText( tr( "Draw as lines (only int layers)" ) );
188  action->setEnabled( false );
189  }
190  menu->addAction( action );
191 
192  // actions
193  action = new QAction( tr( "Actions" ), group );
194  action->setSeparator( true );
195  menu->addAction( action );
196 
197  // load actions
198  group = new QActionGroup( this );
199  group->setExclusive( false );
200  connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
201  action = new QAction( tr( "Reset" ), group );
202  action->setData( QVariant( "Load reset" ) );
203  menu->addAction( action );
204 
205  // these actions have been disabled for api cleanup, restore them eventually
206  // TODO restore these in qgis 2.4
207 #if 0
208  // Load min/max needs 3 params (method, extent, accuracy), cannot put it in single item
209  action = new QAction( tr( "Load min/max" ), group );
210  action->setSeparator( true );
211  menu->addAction( action );
212  action = new QAction( tr( "Estimate (faster)" ), group );
213  action->setData( QVariant( "Load estimate" ) );
214  menu->addAction( action );
215  action = new QAction( tr( "Actual (slower)" ), group );
216  action->setData( QVariant( "Load actual" ) );
217  menu->addAction( action );
218  action = new QAction( tr( "Current extent" ), group );
219  action->setData( QVariant( "Load extent" ) );
220  menu->addAction( action );
221  action = new QAction( tr( "Use stddev (1.0)" ), group );
222  action->setData( QVariant( "Load 1 stddev" ) );
223  menu->addAction( action );
224  action = new QAction( tr( "Use stddev (custom)" ), group );
225  action->setData( QVariant( "Load stddev" ) );
226  menu->addAction( action );
227  action = new QAction( tr( "Load for each band" ), group );
228  action->setData( QVariant( "Load apply all" ) );
229  action->setCheckable( true );
230  action->setChecked( mHistoLoadApplyAll );
231  menu->addAction( action );
232 #endif
233 
234  //others
235  action = new QAction( tr( "Recompute Histogram" ), group );
236  action->setData( QVariant( "Compute histogram" ) );
237  menu->addAction( action );
238 
239  }
240 
241 } // QgsRasterHistogramWidget ctor
242 
243 void QgsRasterHistogramWidget::setRendererWidget( const QString &name, QgsRasterRendererWidget *rendererWidget )
244 {
245  mRendererName = name;
246  mRendererWidget = rendererWidget;
248  on_cboHistoBand_currentIndexChanged( -1 );
249 }
250 
251 void QgsRasterHistogramWidget::setActive( bool activeFlag )
252 {
253  if ( activeFlag )
254  {
256  on_cboHistoBand_currentIndexChanged( -1 );
257  }
258  else
259  {
260  if ( QApplication::overrideCursor() )
261  QApplication::restoreOverrideCursor();
262  btnHistoMin->setChecked( false );
263  btnHistoMax->setChecked( false );
264  }
265 }
266 
267 void QgsRasterHistogramWidget::on_btnHistoCompute_clicked()
268 {
269 // Histogram computation can be called either by clicking the "Compute Histogram" button
270 // which is only visible if there is no cached histogram or by calling the
271 // "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
272 // to re-calculate the histogram if it has already been calculated
273  computeHistogram( true );
275 }
276 
277 bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
278 {
279 
280  //bool myIgnoreOutOfRangeFlag = true;
281  //bool myThoroughBandScanFlag = false;
282  int myBandCountInt = mRasterLayer->bandCount();
283 
284  // if forceComputeFlag = false make sure raster has cached histogram, else return false
285  if ( ! forceComputeFlag )
286  {
287  for ( int myIteratorInt = 1;
288  myIteratorInt <= myBandCountInt;
289  ++myIteratorInt )
290  {
291  int sampleSize = 250000; // number of sample cells
292  if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
293  {
294  QgsDebugMsg( QString( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
295  return false;
296  }
297  }
298  }
299 
300  // compute histogram
301  stackedWidget2->setCurrentIndex( 1 );
302 
303  std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
304  connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
305  QApplication::setOverrideCursor( Qt::WaitCursor );
306 
307  for ( int myIteratorInt = 1;
308  myIteratorInt <= myBandCountInt;
309  ++myIteratorInt )
310  {
311  int sampleSize = 250000; // number of sample cells
312  mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
313  }
314 
315  // mHistogramProgress->hide();
316  stackedWidget2->setCurrentIndex( 0 );
317  QApplication::restoreOverrideCursor();
318 
319  return true;
320 }
321 
322 
324 {
325  // Explanation:
326  // We use the gdal histogram creation routine is called for each selected
327  // layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
328  // the total number of cells that fit into the range defined by that bin.
329  //
330  // The graph routine below determines the greatest number of pixels in any given
331  // bin in all selected layers, and the min. It then draws a scaled line between min
332  // and max - scaled to image height. 1 line drawn per selected band
333  //
334  int myBandCountInt = mRasterLayer->bandCount();
335 
336 
337  if ( ! computeHistogram( false ) )
338  {
339  QgsDebugMsg( QString( "raster does not have cached histogram" ) );
340  stackedWidget2->setCurrentIndex( 2 );
341  return;
342  }
343 
344  // clear plot
345  mpPlot->detachItems();
346 
347  //ensure all children get removed
348  mpPlot->setAutoDelete( true );
349  mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
350  mpPlot->insertLegend( new QwtLegend(), QwtPlot::BottomLegend );
351  // Set axis titles
352  mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Pixel Value" ) );
353  mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Frequency" ) );
354  mpPlot->setAxisAutoScale( QwtPlot::yLeft );
355 
356  // x axis scale only set after computing global min/max across bands (see below)
357  // add a grid
358  QwtPlotGrid *myGrid = new QwtPlotGrid();
359  myGrid->attach( mpPlot );
360 
361  // make colors list
362  mHistoColors.clear();
363  mHistoColors << Qt::black; // first element, not used
364  QVector<QColor> myColors;
365  myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
366  qsrand( myBandCountInt * 100 ); // make sure colors are always the same for a given band count
367  while ( myColors.size() <= myBandCountInt )
368  {
369  myColors <<
370  QColor( 1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
371  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
372  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ) );
373  }
374  //randomise seed again
375  qsrand( time( nullptr ) );
376 
377  // assign colors to each band, depending on the current RGB/gray band selection
378  // grayscale
379  QList< int > mySelectedBands = rendererSelectedBands();
380  if ( mRendererName == QLatin1String( "singlebandgray" ) )
381  {
382  int myGrayBand = mySelectedBands[0];
383  for ( int i = 1; i <= myBandCountInt; i++ )
384  {
385  if ( i == myGrayBand )
386  {
387  mHistoColors << Qt::darkGray;
388  cboHistoBand->setItemData( i - 1, QColor( Qt::darkGray ), Qt::ForegroundRole );
389  }
390  else
391  {
392  if ( ! myColors.isEmpty() )
393  {
394  mHistoColors << myColors.first();
395  myColors.pop_front();
396  }
397  else
398  {
399  mHistoColors << Qt::black;
400  }
401  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
402  }
403  }
404  }
405  // RGB
406  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
407  {
408  int myRedBand = mySelectedBands[0];
409  int myGreenBand = mySelectedBands[1];
410  int myBlueBand = mySelectedBands[2];
411  // remove RGB, which are reserved for the actual RGB bands
412  // show name of RGB bands in appropriate color in bold
413  myColors.remove( 0, 3 );
414  for ( int i = 1; i <= myBandCountInt; i++ )
415  {
416  QColor myColor;
417  if ( i == myRedBand )
418  myColor = Qt::red;
419  else if ( i == myGreenBand )
420  myColor = Qt::green;
421  else if ( i == myBlueBand )
422  myColor = Qt::blue;
423  else
424  {
425  if ( ! myColors.isEmpty() )
426  {
427  myColor = myColors.first();
428  myColors.pop_front();
429  }
430  else
431  {
432  myColor = Qt::black;
433  }
434  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
435  }
436  if ( i == myRedBand || i == myGreenBand || i == myBlueBand )
437  {
438  cboHistoBand->setItemData( i - 1, myColor, Qt::ForegroundRole );
439  }
440  mHistoColors << myColor;
441  }
442  }
443  else
444  {
445  mHistoColors << myColors;
446  }
447 
448  //
449  //now draw actual graphs
450  //
451 
452  //sometimes there are more bins than needed
453  //we find out the last one that actually has data in it
454  //so we can discard the rest and set the x-axis scales correctly
455  //
456  // scan through to get counts from layers' histograms
457  //
458  mHistoMin = 0;
459  mHistoMax = 0;
460  bool myFirstIteration = true;
461  /* get selected band list, if mHistoShowBands != ShowAll */
462  mySelectedBands = histoSelectedBands();
463  double myBinXStep = 1;
464  double myBinX = 0;
465 
466  for ( int myIteratorInt = 1;
467  myIteratorInt <= myBandCountInt;
468  ++myIteratorInt )
469  {
470  /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
471  if ( mHistoShowBands != ShowAll )
472  {
473  if ( ! mySelectedBands.contains( myIteratorInt ) )
474  continue;
475  }
476 
477  int sampleSize = 250000; // number of sample cells
478 
479  std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
480  connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
481 
482  QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
483 
484  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 ) );
485 
486  Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( myIteratorInt );
487  bool myDrawLines = true;
488  if ( ! mHistoDrawLines &&
489  ( mySrcDataType == Qgis::Byte ||
490  mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
491  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 ) )
492  {
493  myDrawLines = false;
494  }
495 
496  QwtPlotCurve *mypCurve = nullptr;
497  if ( myDrawLines )
498  {
499  mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
500  //mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
501  mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
502  mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
503  }
504 
505  QwtPlotHistogram *mypHisto = 0;
506  if ( ! myDrawLines )
507  {
508  mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( myIteratorInt ) );
509  mypHisto->setRenderHint( QwtPlotItem::RenderAntialiased );
510  //mypHisto->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
511  mypHisto->setPen( QPen( Qt::lightGray ) );
512  // this is needed in order to see the colors in the legend
513  mypHisto->setBrush( QBrush( mHistoColors.at( myIteratorInt ) ) );
514  }
515 
516  QVector<QPointF> data;
517  QVector<QwtIntervalSample> dataHisto;
518 
519  // calculate first bin x value and bin step size if not Byte data
520  if ( mySrcDataType != Qgis::Byte )
521  {
522  myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
523  myBinX = myHistogram.minimum + myBinXStep / 2.0;
524  }
525  else
526  {
527  myBinXStep = 1;
528  myBinX = 0;
529  }
530 
531  for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
532  {
533  int myBinValue = myHistogram.histogramVector.at( myBin );
534  if ( myDrawLines )
535  {
536  data << QPointF( myBinX, myBinValue );
537  }
538  else
539  {
540  dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
541  }
542  myBinX += myBinXStep;
543  }
544 
545  if ( myDrawLines )
546  {
547  mypCurve->setSamples( data );
548  mypCurve->attach( mpPlot );
549  }
550  else
551  {
552  mypHisto->setSamples( dataHisto );
553  mypHisto->attach( mpPlot );
554  }
555 
556  if ( myFirstIteration || mHistoMin > myHistogram.minimum )
557  {
558  mHistoMin = myHistogram.minimum;
559  }
560  if ( myFirstIteration || mHistoMax < myHistogram.maximum )
561  {
562  mHistoMax = myHistogram.maximum;
563  }
564  QgsDebugMsg( QString( "computed histo min = %1 max = %2" ).arg( mHistoMin ).arg( mHistoMax ) );
565  myFirstIteration = false;
566  }
567 
568  if ( mHistoMin < mHistoMax )
569  {
570  // for x axis use band pixel values rather than gdal hist. bin values
571  // subtract -0.5 to prevent rounding errors
572  // see http://www.gdal.org/classGDALRasterBand.html#3f8889607d3b2294f7e0f11181c201c8
573  // fix x range for non-Byte data
574  mpPlot->setAxisScale( QwtPlot::xBottom,
575  mHistoMin - myBinXStep / 2,
576  mHistoMax + myBinXStep / 2 );
577  mpPlot->setEnabled( true );
578  mpPlot->replot();
579 
580  // histo plot markers
581  // memory leak?
582  mHistoMarkerMin = new QwtPlotMarker();
583  mHistoMarkerMin->attach( mpPlot );
584  mHistoMarkerMax = new QwtPlotMarker();
585  mHistoMarkerMax->attach( mpPlot );
586  updateHistoMarkers();
587 
588  // histo picker
589  if ( !mHistoPicker )
590  {
591  mHistoPicker = new QwtPlotPicker( mpPlot->canvas() );
592  // mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
593  mHistoPicker->setTrackerMode( QwtPicker::AlwaysOff );
594  mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
595  mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
596  connect( mHistoPicker, static_cast < void ( QwtPlotPicker::* )( const QPointF & ) > ( &QwtPlotPicker::selected ), this, &QgsRasterHistogramWidget::histoPickerSelected );
597  }
598  mHistoPicker->setEnabled( false );
599 
600  // plot zoomer
601  if ( !mHistoZoomer )
602  {
603  mHistoZoomer = new QwtPlotZoomer( mpPlot->canvas() );
604  mHistoZoomer->setStateMachine( new QwtPickerDragRectMachine );
605  mHistoZoomer->setTrackerMode( QwtPicker::AlwaysOff );
606  }
607  mHistoZoomer->setEnabled( true );
608  }
609  else
610  {
611  mpPlot->setDisabled( true );
612  if ( mHistoPicker )
613  mHistoPicker->setEnabled( false );
614  if ( mHistoZoomer )
615  mHistoZoomer->setEnabled( false );
616  }
617 
618  stackedWidget2->setCurrentIndex( 0 );
619  // icon from http://findicons.com/icon/169577/14_zoom?id=171427
620  mpPlot->canvas()->setCursor( QCursor( QgsApplication::getThemePixmap( QStringLiteral( "/mIconZoom.svg" ) ) ) );
621  // on_cboHistoBand_currentIndexChanged( -1 );
622  QApplication::restoreOverrideCursor();
623 }
624 
626 {
627  if ( !mpPlot )
628  return;
629 
630  QPair< QString, QString> myFileNameAndFilter = QgsGuiUtils::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
631  QFileInfo myInfo( myFileNameAndFilter.first );
632  if ( myInfo.baseName() != QLatin1String( "" ) )
633  {
634  histoSaveAsImage( myFileNameAndFilter.first );
635  }
636 }
637 
638 bool QgsRasterHistogramWidget::histoSaveAsImage( const QString &filename,
639  int width, int height, int quality )
640 {
641  // make sure dir. exists
642  QFileInfo myInfo( filename );
643  QDir myDir( myInfo.dir() );
644  if ( ! myDir.exists() )
645  {
646  QgsDebugMsg( QString( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath(), filename ) );
647  return false;
648  }
649 
650  // prepare the pixmap
651  QPixmap myPixmap( width, height );
652  QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
653  myPixmap.fill( Qt::white ); // Qt::transparent ?
654 
655  QwtPlotRenderer myRenderer;
656  myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
657  QwtPlotRenderer::DiscardCanvasBackground );
658  myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
659 
660  QPainter myPainter;
661  myPainter.begin( &myPixmap );
662  myRenderer.render( mpPlot, &myPainter, myQRect );
663  myPainter.end();
664 
665  // save pixmap to file
666  myPixmap.save( filename, nullptr, quality );
667 
668  // should do more error checking
669  return true;
670 }
671 
673 {
674  cboHistoBand->setCurrentIndex( bandNo - 1 );
675 }
676 
677 void QgsRasterHistogramWidget::on_cboHistoBand_currentIndexChanged( int index )
678 {
679  if ( mHistoShowBands == ShowSelected )
681 
682  // get the current index value, index can be -1
683  index = cboHistoBand->currentIndex();
684  if ( mHistoPicker )
685  {
686  mHistoPicker->setEnabled( false );
687  mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
688  }
689  if ( mHistoZoomer )
690  mHistoZoomer->setEnabled( true );
691  btnHistoMin->setEnabled( true );
692  btnHistoMax->setEnabled( true );
693 
694  QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
695  leHistoMin->setText( myMinMax.first );
696  leHistoMax->setText( myMinMax.second );
697 
698  applyHistoMin();
699  applyHistoMax();
700 }
701 
702 void QgsRasterHistogramWidget::histoActionTriggered( QAction *action )
703 {
704  if ( ! action )
705  return;
706  histoAction( action->data().toString(), action->isChecked() );
707 }
708 
709 void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
710 {
711  if ( actionName == QLatin1String( "" ) )
712  return;
713 
714  // this approach is a bit of a hack, but this way we don't have to define slots for each action
715  QgsDebugMsg( QString( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
716 
717  // checkeable actions
718  if ( actionName == QLatin1String( "Show markers" ) )
719  {
720  mHistoShowMarkers = actionFlag;
721  QgsSettings settings;
722  settings.setValue( QStringLiteral( "Raster/histogram/showMarkers" ), mHistoShowMarkers );
723  updateHistoMarkers();
724  return;
725  }
726  else if ( actionName == QLatin1String( "Zoom min_max" ) )
727  {
728  mHistoZoomToMinMax = actionFlag;
729  QgsSettings settings;
730  settings.setValue( QStringLiteral( "Raster/histogram/zoomToMinMax" ), mHistoZoomToMinMax );
731  return;
732  }
733  else if ( actionName == QLatin1String( "Update min_max" ) )
734  {
735  mHistoUpdateStyleToMinMax = actionFlag;
736  QgsSettings settings;
737  settings.setValue( QStringLiteral( "Raster/histogram/updateStyleToMinMax" ), mHistoUpdateStyleToMinMax );
738  return;
739  }
740  else if ( actionName == QLatin1String( "Show all" ) )
741  {
742  mHistoShowBands = ShowAll;
743  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
745  return;
746  }
747  else if ( actionName == QLatin1String( "Show selected" ) )
748  {
749  mHistoShowBands = ShowSelected;
750  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
752  return;
753  }
754  else if ( actionName == QLatin1String( "Show RGB" ) )
755  {
756  mHistoShowBands = ShowRGB;
757  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
759  return;
760  }
761  else if ( actionName == QLatin1String( "Draw lines" ) )
762  {
763  mHistoDrawLines = actionFlag;
764  QgsSettings settings;
765  settings.setValue( QStringLiteral( "Raster/histogram/drawLines" ), mHistoDrawLines );
766  on_btnHistoCompute_clicked(); // refresh
767  return;
768  }
769 #if 0
770  else if ( actionName == "Load apply all" )
771  {
772  mHistoLoadApplyAll = actionFlag;
773  settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
774  return;
775  }
776 #endif
777  // Load actions
778  // TODO - separate calculations from rendererwidget so we can do them without
779  else if ( actionName.left( 5 ) == QLatin1String( "Load " ) && mRendererWidget )
780  {
781  QVector<int> myBands;
782  bool ok = false;
783 
784 #if 0
785  double minMaxValues[2];
786 
787  // find which band(s) need updating (all or current)
788  if ( mHistoLoadApplyAll )
789  {
790  int myBandCountInt = mRasterLayer->bandCount();
791  for ( int i = 1; i <= myBandCountInt; i++ )
792  {
793  if ( i != cboHistoBand->currentIndex() + 1 )
794  myBands << i;
795  }
796  }
797 #endif
798 
799  // add current band to the end
800  myBands << cboHistoBand->currentIndex() + 1;
801 
802  // get stddev value once if needed
803 #if 0
804  double myStdDev = 1.0;
805  if ( actionName == "Load stddev" )
806  {
807  myStdDev = mRendererWidget->stdDev().toDouble();
808  }
809 #endif
810 
811  // don't update markers every time
812  leHistoMin->blockSignals( true );
813  leHistoMax->blockSignals( true );
814 
815  // process each band
816  Q_FOREACH ( int bandNo, myBands )
817  {
818  ok = false;
819 #if 0
820  if ( actionName == "Load actual" )
821  {
822  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
823  bandNo, minMaxValues );
824  }
825  else if ( actionName == "Load estimate" )
826  {
827  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
828  bandNo, minMaxValues );
829  }
830  else if ( actionName == "Load extent" )
831  {
832  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
833  bandNo, minMaxValues );
834  }
835  else if ( actionName == "Load 1 stddev" ||
836  actionName == "Load stddev" )
837  {
838  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, bandNo, minMaxValues );
839  }
840 #endif
841 
842  // apply current item
843  cboHistoBand->setCurrentIndex( bandNo - 1 );
844  if ( !ok || actionName == QLatin1String( "Load reset" ) )
845  {
846  leHistoMin->clear();
847  leHistoMax->clear();
848 #if 0
849  // TODO - fix gdal provider: changes data type when nodata value is not found
850  // this prevents us from getting proper min and max values here
852  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
854  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
855  }
856  else
857  {
858  leHistoMin->setText( QString::number( minMaxValues[0] ) );
859  leHistoMax->setText( QString::number( minMaxValues[1] ) );
860 #endif
861  }
862  applyHistoMin();
863  applyHistoMax();
864  }
865  // update markers
866  leHistoMin->blockSignals( false );
867  leHistoMax->blockSignals( false );
868  updateHistoMarkers();
869  }
870  else if ( actionName == QLatin1String( "Compute histogram" ) )
871  {
872  on_btnHistoCompute_clicked();
873  }
874  else
875  {
876  QgsDebugMsg( "Invalid action " + actionName );
877  return;
878  }
879 }
880 
881 void QgsRasterHistogramWidget::applyHistoMin()
882 {
883  if ( ! mRendererWidget )
884  return;
885 
886  int bandNo = cboHistoBand->currentIndex() + 1;
887  QList< int > mySelectedBands = rendererSelectedBands();
888  QString min;
889  for ( int i = 0; i <= mySelectedBands.size(); i++ )
890  {
891  if ( bandNo == mRendererWidget->selectedBand( i ) )
892  {
893  min = leHistoMin->text();
894  if ( mHistoUpdateStyleToMinMax && mRendererWidget->min( i ) != min )
895  {
896  mRendererWidget->setMin( min, i );
897  if ( mRendererWidget->minMaxWidget() )
898  {
899  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
900  }
901  }
902  }
903  }
904 
905  updateHistoMarkers();
906 
907  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
908  {
909  QRectF rect = mHistoZoomer->zoomRect();
910  rect.setLeft( min.toDouble() );
911  mHistoZoomer->zoom( rect );
912  }
913  emit widgetChanged();
914 }
915 
916 void QgsRasterHistogramWidget::applyHistoMax()
917 {
918  if ( ! mRendererWidget )
919  return;
920 
921  int bandNo = cboHistoBand->currentIndex() + 1;
922  QList< int > mySelectedBands = rendererSelectedBands();
923  QString max;
924  for ( int i = 0; i <= mySelectedBands.size(); i++ )
925  {
926  if ( bandNo == mRendererWidget->selectedBand( i ) )
927  {
928  max = leHistoMax->text();
929  if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
930  {
931  mRendererWidget->setMax( max, i );
932  if ( mRendererWidget->minMaxWidget() )
933  {
934  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
935  }
936  }
937  }
938  }
939 
940  updateHistoMarkers();
941 
942  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
943  {
944  QRectF rect = mHistoZoomer->zoomRect();
945  rect.setRight( max.toDouble() );
946  mHistoZoomer->zoom( rect );
947  }
948  emit widgetChanged();
949 }
950 
951 void QgsRasterHistogramWidget::on_btnHistoMin_toggled()
952 {
953  if ( mpPlot && mHistoPicker )
954  {
955  if ( QApplication::overrideCursor() )
956  QApplication::restoreOverrideCursor();
957  if ( btnHistoMin->isChecked() )
958  {
959  btnHistoMax->setChecked( false );
960  QApplication::setOverrideCursor( Qt::PointingHandCursor );
961  }
962  if ( mHistoZoomer )
963  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
964  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
965  }
966  updateHistoMarkers();
967 }
968 
969 void QgsRasterHistogramWidget::on_btnHistoMax_toggled()
970 {
971  if ( mpPlot && mHistoPicker )
972  {
973  if ( QApplication::overrideCursor() )
974  QApplication::restoreOverrideCursor();
975  if ( btnHistoMax->isChecked() )
976  {
977  btnHistoMin->setChecked( false );
978  QApplication::setOverrideCursor( Qt::PointingHandCursor );
979  }
980  if ( mHistoZoomer )
981  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
982  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
983  }
984  updateHistoMarkers();
985 }
986 
987 // local function used by histoPickerSelected(), to get a rounded picked value
988 // this is sensitive and may not always be correct, needs more testing
989 QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
990 {
991  if ( !scale ) return QLatin1String( "" );
992 
993  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
994  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
995  double diff = ( minorTicks[1] - minorTicks[0] ) / div;
996  double min = majorTicks[0] - diff;
997  if ( min > target )
998  min -= ( majorTicks[1] - majorTicks[0] );
999  double max = scale->upperBound();
1000  double closest = target;
1001  double current = min;
1002 
1003  while ( current < max )
1004  {
1005  current += diff;
1006  if ( current > target )
1007  {
1008  closest = ( qAbs( target - current + diff ) < qAbs( target - current ) ) ? current - diff : current;
1009  break;
1010  }
1011  }
1012 
1013  // QgsDebugMsg( QString( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1014  return QString::number( closest );
1015 }
1016 
1017 void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1018 {
1019  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1020  {
1021  const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1022 
1023  if ( btnHistoMin->isChecked() )
1024  {
1025  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1026  applyHistoMin();
1027  btnHistoMin->setChecked( false );
1028  }
1029  else // if ( btnHistoMax->isChecked() )
1030  {
1031  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1032  applyHistoMax();
1033  btnHistoMax->setChecked( false );
1034  }
1035  }
1036  if ( QApplication::overrideCursor() )
1037  QApplication::restoreOverrideCursor();
1038 }
1039 
1040 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1041 {
1042  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1043 }
1044 
1045 void QgsRasterHistogramWidget::updateHistoMarkers()
1046 {
1047  // hack to not update markers
1048  if ( leHistoMin->signalsBlocked() )
1049  return;
1050  // todo error checking
1051  if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1052  return;
1053 
1054  int bandNo = cboHistoBand->currentIndex() + 1;
1055  QList< int > mySelectedBands = histoSelectedBands();
1056 
1057  if ( ( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1058  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( bandNo ) ) )
1059  {
1060  mHistoMarkerMin->hide();
1061  mHistoMarkerMax->hide();
1062  mpPlot->replot();
1063  return;
1064  }
1065 
1066  double minVal = mHistoMin;
1067  double maxVal = mHistoMax;
1068  QString minStr = leHistoMin->text();
1069  QString maxStr = leHistoMax->text();
1070  if ( minStr != QLatin1String( "" ) )
1071  minVal = minStr.toDouble();
1072  if ( maxStr != QLatin1String( "" ) )
1073  maxVal = maxStr.toDouble();
1074 
1075  QPen linePen = QPen( mHistoColors.at( bandNo ) );
1076  linePen.setStyle( Qt::DashLine );
1077  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1078  mHistoMarkerMin->setLinePen( linePen );
1079  mHistoMarkerMin->setXValue( minVal );
1080  mHistoMarkerMin->show();
1081  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1082  mHistoMarkerMax->setLinePen( linePen );
1083  mHistoMarkerMax->setXValue( maxVal );
1084  mHistoMarkerMax->show();
1085 
1086  mpPlot->replot();
1087 }
1088 
1089 
1090 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1091 {
1092  QList< int > mySelectedBands;
1093 
1094  if ( mHistoShowBands != ShowAll )
1095  {
1096  if ( mHistoShowBands == ShowSelected )
1097  {
1098  mySelectedBands << cboHistoBand->currentIndex() + 1;
1099  }
1100  else if ( mHistoShowBands == ShowRGB )
1101  {
1102  mySelectedBands = rendererSelectedBands();
1103  }
1104  }
1105 
1106  return mySelectedBands;
1107 }
1108 
1109 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1110 {
1111  QList< int > mySelectedBands;
1112 
1113  if ( ! mRendererWidget )
1114  {
1115  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1116  return mySelectedBands;
1117  }
1118 
1119  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1120  {
1121  mySelectedBands << mRendererWidget->selectedBand();
1122  }
1123  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1124  {
1125  for ( int i = 0; i <= 2; i++ )
1126  {
1127  mySelectedBands << mRendererWidget->selectedBand( i );
1128  }
1129  }
1130 
1131  return mySelectedBands;
1132 }
1133 
1134 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1135 {
1136  QPair< QString, QString > myMinMax;
1137 
1138  if ( ! mRendererWidget )
1139  return myMinMax;
1140 
1141  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1142  {
1143  if ( bandNo == mRendererWidget->selectedBand() )
1144  {
1145  myMinMax.first = mRendererWidget->min();
1146  myMinMax.second = mRendererWidget->max();
1147  }
1148  }
1149  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1150  {
1151  for ( int i = 0; i <= 2; i++ )
1152  {
1153  if ( bandNo == mRendererWidget->selectedBand( i ) )
1154  {
1155  myMinMax.first = mRendererWidget->min( i );
1156  myMinMax.second = mRendererWidget->max( i );
1157  break;
1158  }
1159  }
1160  }
1161 
1162  // TODO - there are 2 definitions of raster data type that should be unified
1163  // QgsRasterDataProvider::DataType and Qgis::DataType
1164  // TODO - fix gdal provider: changes data type when nodata value is not found
1165  // this prevents us from getting proper min and max values here
1166  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1167  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1168  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1169  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1170 
1171  // if we get an empty result, fill with default value (histo min/max)
1172  if ( myMinMax.first.isEmpty() )
1173  myMinMax.first = QString::number( mHistoMin );
1174  if ( myMinMax.second.isEmpty() )
1175  myMinMax.second = QString::number( mHistoMax );
1176 
1177  QgsDebugMsg( QString( "bandNo %1 got min/max [%2] [%3]" ).arg( bandNo ).arg( myMinMax.first, myMinMax.second ) );
1178 
1179  return myMinMax;
1180 }
1181 
1183 {
1184 
1185 }
A panel widget that can be shown in the map style dock.
A rectangle specified with double values.
Definition: qgsrectangle.h:38
void setActive(bool activeFlag)
Activate the histogram widget.
Thirty two bit signed integer (qint32)
Definition: qgis.h:81
void setRendererWidget(const QString &name, QgsRasterRendererWidget *rendererWidget=nullptr)
Set the renderer widget (or just its name if there is no widget)
virtual bool hasHistogram(int bandNo, int binCount, double minimum=std::numeric_limits< double >::quiet_NaN(), double maximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, bool includeOutOfRange=false)
Returns true if histogram is available (cached, already calculated)
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
int bandCount() const
Get the number of bands in this layer.
virtual QgsRasterMinMaxWidget * minMaxWidget()
Return min/max widget when it exists.
static double minimumValuePossible(Qgis::DataType)
Helper function that returns the minimum possible value for a GDAL data type.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:54
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QPointF QwtDoublePoint
QgsRasterHistogramWidget(QgsRasterLayer *lyr, QWidget *parent=0)
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:80
DataType
Raster data types.
Definition: qgis.h:74
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
virtual QgsRasterHistogram histogram(int bandNo, int binCount=0, double minimum=std::numeric_limits< double >::quiet_NaN(), double maximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, bool includeOutOfRange=false, QgsRasterBlockFeedback *feedback=nullptr)
Get histogram.
Sixteen bit signed integer (qint16)
Definition: qgis.h:79
static QPixmap getThemePixmap(const QString &name)
Helper to get a theme icon as a pixmap.
QPair< QString, QString > GUI_EXPORT getSaveAsImageName(QWidget *parent, const QString &message, const QString &defaultFilename)
A helper function to get an image name from the user.
Definition: qgsguiutils.cpp:87
virtual Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual void setMax(const QString &value, int index=0)
QgsRasterDataProvider * dataProvider() override
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
virtual Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual QString min(int index=0)
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
QString bandName(int bandNoInt) const
Get the name of a band given its number.
virtual int selectedBand(int index=0)
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:78
bool histoSaveAsImage(const QString &filename, int width=600, int height=600, int quality=-1)
Save the histogram as an image to disk.
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:452
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:437
void widgetChanged()
Emitted when the widget state changes.
void on_mSaveAsImageButton_clicked()
This slot lets you save the histogram as an image to disk.
void userHasSetManualMinMaxValues()
Uncheck cumulative cut, min/max, std-dev radio buttons.
static double maximumValuePossible(Qgis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
The QgsRasterHistogram is a container for histogram of a single raster band.
virtual QString max(int index=0)
virtual void setMin(const QString &value, int index=0)
QString findClosestTickVal(double target, const QwtScaleDiv *scale, int div=100)
Feedback object tailored for raster block reading.
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
Eight bit unsigned integer (quint8)
Definition: qgis.h:77