QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 bandNumber = 1;
522  bandNumber <= myBandCountInt;
523  ++bandNumber )
524  {
525  /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
526  if ( mHistoShowBands != ShowAll )
527  {
528  if ( ! mySelectedBands.contains( bandNumber ) )
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(), bandNumber, sampleSize );
538  const QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( bandNumber, 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( bandNumber ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myHistogram.binCount ) );
541 
542  const Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( bandNumber );
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( bandNumber ) );
556  //mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
557  mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
558  mypCurve->setPen( QPen( mHistoColors.at( bandNumber ) ) );
559  }
560 
561  QwtPlotHistogram *mypHisto = nullptr;
562  if ( ! myDrawLines )
563  {
564  mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( bandNumber ) );
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( bandNumber ) ) );
570  }
571 
572  QVector<QPointF> data;
573  QVector<QwtIntervalSample> dataHisto;
574 
575  // calculate first bin x value and bin step size
576  myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
577  myBinX = myHistogram.minimum + myBinXStep / 2.0;
578 
579  for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
580  {
581  const int myBinValue = myHistogram.histogramVector.at( myBin );
582  if ( myDrawLines )
583  {
584  data << QPointF( myBinX, myBinValue );
585  }
586  else
587  {
588  dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
589  }
590  myBinX += myBinXStep;
591  }
592 
593  if ( myDrawLines )
594  {
595  mypCurve->setSamples( data );
596  mypCurve->attach( mpPlot );
597  }
598  else
599  {
600  mypHisto->setSamples( dataHisto );
601  mypHisto->attach( mpPlot );
602  }
603 
604  if ( myFirstIteration || mHistoMin > myHistogram.minimum )
605  {
606  mHistoMin = myHistogram.minimum;
607  }
608  if ( myFirstIteration || mHistoMax < myHistogram.maximum )
609  {
610  mHistoMax = myHistogram.maximum;
611  }
612  QgsDebugMsg( QStringLiteral( "computed histo min = %1 max = %2" ).arg( mHistoMin ).arg( mHistoMax ) );
613  myFirstIteration = false;
614  }
615 
616  if ( mHistoMin < mHistoMax )
617  {
618  // for x axis use band pixel values rather than gdal hist. bin values
619  // subtract -0.5 to prevent rounding errors
620  // see http://www.gdal.org/classGDALRasterBand.html#3f8889607d3b2294f7e0f11181c201c8
621  // fix x range for non-Byte data
622  mpPlot->setAxisScale( QwtPlot::xBottom,
623  mHistoMin - myBinXStep / 2,
624  mHistoMax + myBinXStep / 2 );
625  mpPlot->setEnabled( true );
626  mpPlot->replot();
627 
628  // histo plot markers
629  // memory leak?
630  mHistoMarkerMin = new QwtPlotMarker();
631  mHistoMarkerMin->attach( mpPlot );
632  mHistoMarkerMax = new QwtPlotMarker();
633  mHistoMarkerMax->attach( mpPlot );
634  updateHistoMarkers();
635 
636  // histo picker
637  if ( !mHistoPicker )
638  {
639  mHistoPicker = new QwtPlotPicker( mpPlot->canvas() );
640  // mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
641  mHistoPicker->setTrackerMode( QwtPicker::AlwaysOff );
642  mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
643  mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
644  connect( mHistoPicker, static_cast < void ( QwtPlotPicker::* )( const QPointF & ) > ( &QwtPlotPicker::selected ), this, &QgsRasterHistogramWidget::histoPickerSelected );
645  }
646  mHistoPicker->setEnabled( false );
647 
648  // plot zoomer
649  if ( !mHistoZoomer )
650  {
651  mHistoZoomer = new QwtPlotZoomer( mpPlot->canvas() );
652  mHistoZoomer->setStateMachine( new QwtPickerDragRectMachine );
653  mHistoZoomer->setTrackerMode( QwtPicker::AlwaysOff );
654  }
655  mHistoZoomer->setEnabled( true );
656  }
657  else
658  {
659  mpPlot->setDisabled( true );
660  if ( mHistoPicker )
661  mHistoPicker->setEnabled( false );
662  if ( mHistoZoomer )
663  mHistoZoomer->setEnabled( false );
664  }
665 
666  stackedWidget2->setCurrentIndex( 0 );
667  // icon from http://findicons.com/icon/169577/14_zoom?id=171427
668  mpPlot->canvas()->setCursor( QCursor( QgsApplication::getThemePixmap( QStringLiteral( "/mIconZoom.svg" ) ) ) );
669  // cboHistoBand_currentIndexChanged( -1 );
670  QApplication::restoreOverrideCursor();
671 }
672 
673 void QgsRasterHistogramWidget::mSaveAsImageButton_clicked()
674 {
675  if ( !mpPlot )
676  return;
677 
678  const QPair< QString, QString> myFileNameAndFilter = QgsGuiUtils::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
679  const QFileInfo myInfo( myFileNameAndFilter.first );
680  if ( !myInfo.baseName().isEmpty() )
681  {
682  histoSaveAsImage( myFileNameAndFilter.first );
683  }
684 }
685 
686 bool QgsRasterHistogramWidget::histoSaveAsImage( const QString &filename,
687  int width, int height, int quality )
688 {
689  // make sure dir. exists
690  const QFileInfo myInfo( filename );
691  const QDir myDir( myInfo.dir() );
692  if ( ! myDir.exists() )
693  {
694  QgsDebugMsg( QStringLiteral( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath(), filename ) );
695  return false;
696  }
697 
698  // prepare the pixmap
699  QPixmap myPixmap( width, height );
700  const QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
701  myPixmap.fill( Qt::white ); // Qt::transparent ?
702 
703  QwtPlotRenderer myRenderer;
704  myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
705  QwtPlotRenderer::DiscardCanvasBackground );
706  myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
707 
708  QPainter myPainter;
709  myPainter.begin( &myPixmap );
710  myRenderer.render( mpPlot, &myPainter, myQRect );
711  myPainter.end();
712 
713  // save pixmap to file
714  myPixmap.save( filename, nullptr, quality );
715 
716  // should do more error checking
717  return true;
718 }
719 
721 {
722  cboHistoBand->setCurrentIndex( bandNo - 1 );
723 }
724 
725 void QgsRasterHistogramWidget::cboHistoBand_currentIndexChanged( int index )
726 {
727  if ( mHistoShowBands == ShowSelected )
729 
730  // get the current index value, index can be -1
731  index = cboHistoBand->currentIndex();
732  if ( mHistoPicker )
733  {
734  mHistoPicker->setEnabled( false );
735  mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
736  }
737  if ( mHistoZoomer )
738  mHistoZoomer->setEnabled( true );
739  btnHistoMin->setEnabled( true );
740  btnHistoMax->setEnabled( true );
741 
742  const QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
743  leHistoMin->setText( myMinMax.first );
744  leHistoMax->setText( myMinMax.second );
745 
746  applyHistoMin();
747  applyHistoMax();
748 }
749 
750 void QgsRasterHistogramWidget::histoActionTriggered( QAction *action )
751 {
752  if ( ! action )
753  return;
754  histoAction( action->data().toString(), action->isChecked() );
755 }
756 
757 void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
758 {
759  if ( actionName.isEmpty() )
760  return;
761 
762  // this approach is a bit of a hack, but this way we don't have to define slots for each action
763  QgsDebugMsg( QStringLiteral( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
764 
765  // checkeable actions
766  if ( actionName == QLatin1String( "Show markers" ) )
767  {
768  mHistoShowMarkers = actionFlag;
769  QgsSettings settings;
770  settings.setValue( QStringLiteral( "Raster/histogram/showMarkers" ), mHistoShowMarkers );
771  updateHistoMarkers();
772  return;
773  }
774  else if ( actionName == QLatin1String( "Zoom min_max" ) )
775  {
776  mHistoZoomToMinMax = actionFlag;
777  QgsSettings settings;
778  settings.setValue( QStringLiteral( "Raster/histogram/zoomToMinMax" ), mHistoZoomToMinMax );
779  return;
780  }
781  else if ( actionName == QLatin1String( "Update min_max" ) )
782  {
783  mHistoUpdateStyleToMinMax = actionFlag;
784  QgsSettings settings;
785  settings.setValue( QStringLiteral( "Raster/histogram/updateStyleToMinMax" ), mHistoUpdateStyleToMinMax );
786  return;
787  }
788  else if ( actionName == QLatin1String( "Show all" ) )
789  {
790  mHistoShowBands = ShowAll;
791  // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
793  return;
794  }
795  else if ( actionName == QLatin1String( "Show selected" ) )
796  {
797  mHistoShowBands = ShowSelected;
798  // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
800  return;
801  }
802  else if ( actionName == QLatin1String( "Show RGB" ) )
803  {
804  mHistoShowBands = ShowRGB;
805  // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
807  return;
808  }
809  else if ( actionName == QLatin1String( "Draw lines" ) )
810  {
811  mHistoDrawLines = actionFlag;
812  QgsSettings settings;
813  settings.setValue( QStringLiteral( "Raster/histogram/drawLines" ), mHistoDrawLines );
814  btnHistoCompute_clicked(); // refresh
815  return;
816  }
817 #if 0
818  else if ( actionName == "Load apply all" )
819  {
820  mHistoLoadApplyAll = actionFlag;
821  settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
822  return;
823  }
824 #endif
825  // Load actions
826  // TODO - separate calculations from rendererwidget so we can do them without
827  else if ( actionName.left( 5 ) == QLatin1String( "Load " ) && mRendererWidget )
828  {
829  QVector<int> myBands;
830  bool ok = false;
831 
832 #if 0
833  double minMaxValues[2];
834 
835  // find which band(s) need updating (all or current)
836  if ( mHistoLoadApplyAll )
837  {
838  int myBandCountInt = mRasterLayer->bandCount();
839  for ( int i = 1; i <= myBandCountInt; i++ )
840  {
841  if ( i != cboHistoBand->currentIndex() + 1 )
842  myBands << i;
843  }
844  }
845 #endif
846 
847  // add current band to the end
848  myBands << cboHistoBand->currentIndex() + 1;
849 
850  // get stddev value once if needed
851 #if 0
852  double myStdDev = 1.0;
853  if ( actionName == "Load stddev" )
854  {
855  myStdDev = mRendererWidget->stdDev().toDouble();
856  }
857 #endif
858 
859  // don't update markers every time
860  leHistoMin->blockSignals( true );
861  leHistoMax->blockSignals( true );
862 
863  // process each band
864  const auto constMyBands = myBands;
865  for ( const int bandNo : constMyBands )
866  {
867  ok = false;
868 #if 0
869  if ( actionName == "Load actual" )
870  {
871  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
872  bandNo, minMaxValues );
873  }
874  else if ( actionName == "Load estimate" )
875  {
876  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
877  bandNo, minMaxValues );
878  }
879  else if ( actionName == "Load extent" )
880  {
881  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
882  bandNo, minMaxValues );
883  }
884  else if ( actionName == "Load 1 stddev" ||
885  actionName == "Load stddev" )
886  {
887  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, bandNo, minMaxValues );
888  }
889 #endif
890 
891  // apply current item
892  cboHistoBand->setCurrentIndex( bandNo - 1 );
893  if ( !ok || actionName == QLatin1String( "Load reset" ) )
894  {
895  leHistoMin->clear();
896  leHistoMax->clear();
897 #if 0
898  // TODO - fix gdal provider: changes data type when nodata value is not found
899  // this prevents us from getting proper min and max values here
901  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
903  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
904  }
905  else
906  {
907  leHistoMin->setText( QString::number( minMaxValues[0] ) );
908  leHistoMax->setText( QString::number( minMaxValues[1] ) );
909 #endif
910  }
911  applyHistoMin();
912  applyHistoMax();
913  }
914  // update markers
915  leHistoMin->blockSignals( false );
916  leHistoMax->blockSignals( false );
917  updateHistoMarkers();
918  }
919  else if ( actionName == QLatin1String( "Compute histogram" ) )
920  {
921  btnHistoCompute_clicked();
922  }
923  else
924  {
925  QgsDebugMsg( "Invalid action " + actionName );
926  return;
927  }
928 }
929 
930 void QgsRasterHistogramWidget::applyHistoMin()
931 {
932  if ( ! mRendererWidget )
933  return;
934 
935  const int bandNo = cboHistoBand->currentIndex() + 1;
936  const QList< int > mySelectedBands = rendererSelectedBands();
937  QString min;
938  for ( int i = 0; i <= mySelectedBands.size(); i++ )
939  {
940  if ( bandNo == mRendererWidget->selectedBand( i ) )
941  {
942  min = leHistoMin->text();
943  if ( mHistoUpdateStyleToMinMax && mRendererWidget->min( i ) != min )
944  {
945  mRendererWidget->setMin( min, i );
946  if ( mRendererWidget->minMaxWidget() )
947  {
948  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
949  }
950  }
951  }
952  }
953 
954  updateHistoMarkers();
955 
956  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
957  {
958  QRectF rect = mHistoZoomer->zoomRect();
959  rect.setLeft( min.toDouble() );
960  mHistoZoomer->zoom( rect );
961  }
962  emit widgetChanged();
963 }
964 
965 void QgsRasterHistogramWidget::applyHistoMax()
966 {
967  if ( ! mRendererWidget )
968  return;
969 
970  const int bandNo = cboHistoBand->currentIndex() + 1;
971  const QList< int > mySelectedBands = rendererSelectedBands();
972  QString max;
973  for ( int i = 0; i <= mySelectedBands.size(); i++ )
974  {
975  if ( bandNo == mRendererWidget->selectedBand( i ) )
976  {
977  max = leHistoMax->text();
978  if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
979  {
980  mRendererWidget->setMax( max, i );
981  if ( mRendererWidget->minMaxWidget() )
982  {
983  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
984  }
985  }
986  }
987  }
988 
989  updateHistoMarkers();
990 
991  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
992  {
993  QRectF rect = mHistoZoomer->zoomRect();
994  rect.setRight( max.toDouble() );
995  mHistoZoomer->zoom( rect );
996  }
997  emit widgetChanged();
998 }
999 
1000 void QgsRasterHistogramWidget::btnHistoMin_toggled()
1001 {
1002  if ( mpPlot && mHistoPicker )
1003  {
1004  if ( QApplication::overrideCursor() )
1005  QApplication::restoreOverrideCursor();
1006  if ( btnHistoMin->isChecked() )
1007  {
1008  btnHistoMax->setChecked( false );
1009  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1010  }
1011  if ( mHistoZoomer )
1012  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
1013  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1014  }
1015  updateHistoMarkers();
1016 }
1017 
1018 void QgsRasterHistogramWidget::btnHistoMax_toggled()
1019 {
1020  if ( mpPlot && mHistoPicker )
1021  {
1022  if ( QApplication::overrideCursor() )
1023  QApplication::restoreOverrideCursor();
1024  if ( btnHistoMax->isChecked() )
1025  {
1026  btnHistoMin->setChecked( false );
1027  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1028  }
1029  if ( mHistoZoomer )
1030  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
1031  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1032  }
1033  updateHistoMarkers();
1034 }
1035 
1036 // local function used by histoPickerSelected(), to get a rounded picked value
1037 // this is sensitive and may not always be correct, needs more testing
1038 QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
1039 {
1040  if ( !scale ) return QString();
1041 
1042  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1043  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1044  const double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1045  double min = majorTicks[0] - diff;
1046  if ( min > target )
1047  min -= ( majorTicks[1] - majorTicks[0] );
1048  const double max = scale->upperBound();
1049  double closest = target;
1050  double current = min;
1051 
1052  while ( current < max )
1053  {
1054  current += diff;
1055  if ( current > target )
1056  {
1057  closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current;
1058  break;
1059  }
1060  }
1061 
1062  // QgsDebugMsg( QStringLiteral( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1063  return QLocale().toString( closest );
1064 }
1065 
1066 void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1067 {
1068  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1069  {
1070  const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1071 
1072  if ( btnHistoMin->isChecked() )
1073  {
1074  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1075  applyHistoMin();
1076  btnHistoMin->setChecked( false );
1077  }
1078  else // if ( btnHistoMax->isChecked() )
1079  {
1080  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1081  applyHistoMax();
1082  btnHistoMax->setChecked( false );
1083  }
1084  }
1085  if ( QApplication::overrideCursor() )
1086  QApplication::restoreOverrideCursor();
1087 }
1088 
1089 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1090 {
1091  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1092 }
1093 
1094 void QgsRasterHistogramWidget::updateHistoMarkers()
1095 {
1096  // hack to not update markers
1097  if ( leHistoMin->signalsBlocked() )
1098  return;
1099  // todo error checking
1100  if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1101  return;
1102 
1103  const int bandNo = cboHistoBand->currentIndex() + 1;
1104  const QList< int > mySelectedBands = histoSelectedBands();
1105 
1106  if ( ( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1107  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( bandNo ) ) )
1108  {
1109  mHistoMarkerMin->hide();
1110  mHistoMarkerMax->hide();
1111  mpPlot->replot();
1112  return;
1113  }
1114 
1115  double minVal = mHistoMin;
1116  double maxVal = mHistoMax;
1117  const QString minStr = leHistoMin->text();
1118  const QString maxStr = leHistoMax->text();
1119  if ( !minStr.isEmpty() )
1120  minVal = minStr.toDouble();
1121  if ( !maxStr.isEmpty() )
1122  maxVal = maxStr.toDouble();
1123 
1124  QPen linePen = QPen( mHistoColors.at( bandNo ) );
1125  linePen.setStyle( Qt::DashLine );
1126  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1127  mHistoMarkerMin->setLinePen( linePen );
1128  mHistoMarkerMin->setXValue( minVal );
1129  mHistoMarkerMin->show();
1130  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1131  mHistoMarkerMax->setLinePen( linePen );
1132  mHistoMarkerMax->setXValue( maxVal );
1133  mHistoMarkerMax->show();
1134 
1135  mpPlot->replot();
1136 }
1137 
1138 
1139 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1140 {
1141  QList< int > mySelectedBands;
1142 
1143  if ( mHistoShowBands != ShowAll )
1144  {
1145  if ( mHistoShowBands == ShowSelected )
1146  {
1147  mySelectedBands << cboHistoBand->currentIndex() + 1;
1148  }
1149  else if ( mHistoShowBands == ShowRGB )
1150  {
1151  mySelectedBands = rendererSelectedBands();
1152  }
1153  }
1154 
1155  return mySelectedBands;
1156 }
1157 
1158 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1159 {
1160  QList< int > mySelectedBands;
1161 
1162  if ( ! mRendererWidget )
1163  {
1164  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1165  return mySelectedBands;
1166  }
1167 
1168  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1169  {
1170  mySelectedBands << mRendererWidget->selectedBand();
1171  }
1172  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1173  {
1174  for ( int i = 0; i <= 2; i++ )
1175  {
1176  mySelectedBands << mRendererWidget->selectedBand( i );
1177  }
1178  }
1179 
1180  return mySelectedBands;
1181 }
1182 
1183 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1184 {
1185  QPair< QString, QString > myMinMax;
1186 
1187  if ( ! mRendererWidget )
1188  return myMinMax;
1189 
1190  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1191  {
1192  if ( bandNo == mRendererWidget->selectedBand() )
1193  {
1194  myMinMax.first = mRendererWidget->min();
1195  myMinMax.second = mRendererWidget->max();
1196  }
1197  }
1198  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1199  {
1200  for ( int i = 0; i <= 2; i++ )
1201  {
1202  if ( bandNo == mRendererWidget->selectedBand( i ) )
1203  {
1204  myMinMax.first = mRendererWidget->min( i );
1205  myMinMax.second = mRendererWidget->max( i );
1206  break;
1207  }
1208  }
1209  }
1210 
1211  // TODO - there are 2 definitions of raster data type that should be unified
1212  // QgsRasterDataProvider::DataType and Qgis::DataType
1213  // TODO - fix gdal provider: changes data type when nodata value is not found
1214  // this prevents us from getting proper min and max values here
1215  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1216  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1217  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1218  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1219 
1220  // if we get an empty result, fill with default value (histo min/max)
1221  if ( myMinMax.first.isEmpty() )
1222  myMinMax.first = QLocale().toString( mHistoMin );
1223  if ( myMinMax.second.isEmpty() )
1224  myMinMax.second = QLocale().toString( mHistoMax );
1225 
1226  QgsDebugMsg( QStringLiteral( "bandNo %1 got min/max [%2] [%3]" ).arg( bandNo ).arg( myMinMax.first, myMinMax.second ) );
1227 
1228  return myMinMax;
1229 }
1230 
1232 {
1233 
1234 }
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.
Abstract base class for widgets which configure a QgsRasterRenderer.
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
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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)