QGIS API Documentation  3.2.0-Bonn (bc43194)
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( QLatin1String( "" ), 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( QString( "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( QString( "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( QString( "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( QString( "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( QString( "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( QString( "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", (int)mHistoShowBands );
750  return;
751  }
752  else if ( actionName == QLatin1String( "Show selected" ) )
753  {
754  mHistoShowBands = ShowSelected;
755  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
757  return;
758  }
759  else if ( actionName == QLatin1String( "Show RGB" ) )
760  {
761  mHistoShowBands = ShowRGB;
762  // settings.setValue( "/Raster/histogram/showBands", (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 QLatin1String( "" );
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( QString( "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( QString( "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:98
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)
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
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:97
DataType
Raster data types.
Definition: qgis.h:91
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:96
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.
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:95
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.
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:94
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.