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