QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  Q_FOREACH ( int bandNo, myBands )
822  {
823  ok = false;
824 #if 0
825  if ( actionName == "Load actual" )
826  {
827  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
828  bandNo, minMaxValues );
829  }
830  else if ( actionName == "Load estimate" )
831  {
832  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
833  bandNo, minMaxValues );
834  }
835  else if ( actionName == "Load extent" )
836  {
837  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
838  bandNo, minMaxValues );
839  }
840  else if ( actionName == "Load 1 stddev" ||
841  actionName == "Load stddev" )
842  {
843  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, bandNo, minMaxValues );
844  }
845 #endif
846 
847  // apply current item
848  cboHistoBand->setCurrentIndex( bandNo - 1 );
849  if ( !ok || actionName == QLatin1String( "Load reset" ) )
850  {
851  leHistoMin->clear();
852  leHistoMax->clear();
853 #if 0
854  // TODO - fix gdal provider: changes data type when nodata value is not found
855  // this prevents us from getting proper min and max values here
857  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
859  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
860  }
861  else
862  {
863  leHistoMin->setText( QString::number( minMaxValues[0] ) );
864  leHistoMax->setText( QString::number( minMaxValues[1] ) );
865 #endif
866  }
867  applyHistoMin();
868  applyHistoMax();
869  }
870  // update markers
871  leHistoMin->blockSignals( false );
872  leHistoMax->blockSignals( false );
873  updateHistoMarkers();
874  }
875  else if ( actionName == QLatin1String( "Compute histogram" ) )
876  {
877  btnHistoCompute_clicked();
878  }
879  else
880  {
881  QgsDebugMsg( "Invalid action " + actionName );
882  return;
883  }
884 }
885 
886 void QgsRasterHistogramWidget::applyHistoMin()
887 {
888  if ( ! mRendererWidget )
889  return;
890 
891  int bandNo = cboHistoBand->currentIndex() + 1;
892  QList< int > mySelectedBands = rendererSelectedBands();
893  QString min;
894  for ( int i = 0; i <= mySelectedBands.size(); i++ )
895  {
896  if ( bandNo == mRendererWidget->selectedBand( i ) )
897  {
898  min = leHistoMin->text();
899  if ( mHistoUpdateStyleToMinMax && mRendererWidget->min( i ) != min )
900  {
901  mRendererWidget->setMin( min, i );
902  if ( mRendererWidget->minMaxWidget() )
903  {
904  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
905  }
906  }
907  }
908  }
909 
910  updateHistoMarkers();
911 
912  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
913  {
914  QRectF rect = mHistoZoomer->zoomRect();
915  rect.setLeft( min.toDouble() );
916  mHistoZoomer->zoom( rect );
917  }
918  emit widgetChanged();
919 }
920 
921 void QgsRasterHistogramWidget::applyHistoMax()
922 {
923  if ( ! mRendererWidget )
924  return;
925 
926  int bandNo = cboHistoBand->currentIndex() + 1;
927  QList< int > mySelectedBands = rendererSelectedBands();
928  QString max;
929  for ( int i = 0; i <= mySelectedBands.size(); i++ )
930  {
931  if ( bandNo == mRendererWidget->selectedBand( i ) )
932  {
933  max = leHistoMax->text();
934  if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
935  {
936  mRendererWidget->setMax( max, i );
937  if ( mRendererWidget->minMaxWidget() )
938  {
939  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
940  }
941  }
942  }
943  }
944 
945  updateHistoMarkers();
946 
947  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
948  {
949  QRectF rect = mHistoZoomer->zoomRect();
950  rect.setRight( max.toDouble() );
951  mHistoZoomer->zoom( rect );
952  }
953  emit widgetChanged();
954 }
955 
956 void QgsRasterHistogramWidget::btnHistoMin_toggled()
957 {
958  if ( mpPlot && mHistoPicker )
959  {
960  if ( QApplication::overrideCursor() )
961  QApplication::restoreOverrideCursor();
962  if ( btnHistoMin->isChecked() )
963  {
964  btnHistoMax->setChecked( false );
965  QApplication::setOverrideCursor( Qt::PointingHandCursor );
966  }
967  if ( mHistoZoomer )
968  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
969  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
970  }
971  updateHistoMarkers();
972 }
973 
974 void QgsRasterHistogramWidget::btnHistoMax_toggled()
975 {
976  if ( mpPlot && mHistoPicker )
977  {
978  if ( QApplication::overrideCursor() )
979  QApplication::restoreOverrideCursor();
980  if ( btnHistoMax->isChecked() )
981  {
982  btnHistoMin->setChecked( false );
983  QApplication::setOverrideCursor( Qt::PointingHandCursor );
984  }
985  if ( mHistoZoomer )
986  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
987  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
988  }
989  updateHistoMarkers();
990 }
991 
992 // local function used by histoPickerSelected(), to get a rounded picked value
993 // this is sensitive and may not always be correct, needs more testing
994 QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
995 {
996  if ( !scale ) return QString();
997 
998  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
999  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1000  double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1001  double min = majorTicks[0] - diff;
1002  if ( min > target )
1003  min -= ( majorTicks[1] - majorTicks[0] );
1004  double max = scale->upperBound();
1005  double closest = target;
1006  double current = min;
1007 
1008  while ( current < max )
1009  {
1010  current += diff;
1011  if ( current > target )
1012  {
1013  closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current;
1014  break;
1015  }
1016  }
1017 
1018  // QgsDebugMsg( QStringLiteral( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1019  return QString::number( closest );
1020 }
1021 
1022 void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1023 {
1024  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1025  {
1026  const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1027 
1028  if ( btnHistoMin->isChecked() )
1029  {
1030  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1031  applyHistoMin();
1032  btnHistoMin->setChecked( false );
1033  }
1034  else // if ( btnHistoMax->isChecked() )
1035  {
1036  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1037  applyHistoMax();
1038  btnHistoMax->setChecked( false );
1039  }
1040  }
1041  if ( QApplication::overrideCursor() )
1042  QApplication::restoreOverrideCursor();
1043 }
1044 
1045 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1046 {
1047  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1048 }
1049 
1050 void QgsRasterHistogramWidget::updateHistoMarkers()
1051 {
1052  // hack to not update markers
1053  if ( leHistoMin->signalsBlocked() )
1054  return;
1055  // todo error checking
1056  if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1057  return;
1058 
1059  int bandNo = cboHistoBand->currentIndex() + 1;
1060  QList< int > mySelectedBands = histoSelectedBands();
1061 
1062  if ( ( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1063  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( bandNo ) ) )
1064  {
1065  mHistoMarkerMin->hide();
1066  mHistoMarkerMax->hide();
1067  mpPlot->replot();
1068  return;
1069  }
1070 
1071  double minVal = mHistoMin;
1072  double maxVal = mHistoMax;
1073  QString minStr = leHistoMin->text();
1074  QString maxStr = leHistoMax->text();
1075  if ( !minStr.isEmpty() )
1076  minVal = minStr.toDouble();
1077  if ( !maxStr.isEmpty() )
1078  maxVal = maxStr.toDouble();
1079 
1080  QPen linePen = QPen( mHistoColors.at( bandNo ) );
1081  linePen.setStyle( Qt::DashLine );
1082  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1083  mHistoMarkerMin->setLinePen( linePen );
1084  mHistoMarkerMin->setXValue( minVal );
1085  mHistoMarkerMin->show();
1086  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1087  mHistoMarkerMax->setLinePen( linePen );
1088  mHistoMarkerMax->setXValue( maxVal );
1089  mHistoMarkerMax->show();
1090 
1091  mpPlot->replot();
1092 }
1093 
1094 
1095 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1096 {
1097  QList< int > mySelectedBands;
1098 
1099  if ( mHistoShowBands != ShowAll )
1100  {
1101  if ( mHistoShowBands == ShowSelected )
1102  {
1103  mySelectedBands << cboHistoBand->currentIndex() + 1;
1104  }
1105  else if ( mHistoShowBands == ShowRGB )
1106  {
1107  mySelectedBands = rendererSelectedBands();
1108  }
1109  }
1110 
1111  return mySelectedBands;
1112 }
1113 
1114 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1115 {
1116  QList< int > mySelectedBands;
1117 
1118  if ( ! mRendererWidget )
1119  {
1120  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1121  return mySelectedBands;
1122  }
1123 
1124  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1125  {
1126  mySelectedBands << mRendererWidget->selectedBand();
1127  }
1128  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1129  {
1130  for ( int i = 0; i <= 2; i++ )
1131  {
1132  mySelectedBands << mRendererWidget->selectedBand( i );
1133  }
1134  }
1135 
1136  return mySelectedBands;
1137 }
1138 
1139 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1140 {
1141  QPair< QString, QString > myMinMax;
1142 
1143  if ( ! mRendererWidget )
1144  return myMinMax;
1145 
1146  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1147  {
1148  if ( bandNo == mRendererWidget->selectedBand() )
1149  {
1150  myMinMax.first = mRendererWidget->min();
1151  myMinMax.second = mRendererWidget->max();
1152  }
1153  }
1154  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1155  {
1156  for ( int i = 0; i <= 2; i++ )
1157  {
1158  if ( bandNo == mRendererWidget->selectedBand( i ) )
1159  {
1160  myMinMax.first = mRendererWidget->min( i );
1161  myMinMax.second = mRendererWidget->max( i );
1162  break;
1163  }
1164  }
1165  }
1166 
1167  // TODO - there are 2 definitions of raster data type that should be unified
1168  // QgsRasterDataProvider::DataType and Qgis::DataType
1169  // TODO - fix gdal provider: changes data type when nodata value is not found
1170  // this prevents us from getting proper min and max values here
1171  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1172  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1173  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1174  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1175 
1176  // if we get an empty result, fill with default value (histo min/max)
1177  if ( myMinMax.first.isEmpty() )
1178  myMinMax.first = QString::number( mHistoMin );
1179  if ( myMinMax.second.isEmpty() )
1180  myMinMax.second = QString::number( mHistoMax );
1181 
1182  QgsDebugMsg( QStringLiteral( "bandNo %1 got min/max [%2] [%3]" ).arg( bandNo ).arg( myMinMax.first, myMinMax.second ) );
1183 
1184  return myMinMax;
1185 }
1186 
1188 {
1189 
1190 }
A panel widget that can be shown in the map style dock.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
void setActive(bool activeFlag)
Activate the histogram widget.
Thirty two bit signed integer (qint32)
Definition: qgis.h:99
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
virtual QgsRasterMinMaxWidget * minMaxWidget()
Returns 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:58
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QPointF QwtDoublePoint
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:98
DataType
Raster data types.
Definition: qgis.h:92
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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:97
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:88
virtual void setMax(const QString &value, int index=0)
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
virtual QString min(int index=0)
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
int bandCount() const
Returns the number of bands in this layer.
virtual int selectedBand(int index=0)
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:96
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.
static double maximumValuePossible(Qgis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
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:95
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.