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