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