QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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::Byte ||
100  mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
101  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::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::Byte ? 0 :
293  rasterInterface->bandStatistics( bandNo, QgsRasterBandStats::Min, QgsRectangle(), sampleSize ).minimumValue;
294  const double statsMax = mySrcDataType == Qgis::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::Int16 || mySrcDataType == Qgis::Int32 ||
318  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::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::Byte ||
543  mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
544  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::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::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 }
SAMPLE_SIZE
constexpr int SAMPLE_SIZE
Definition: qgsrasterhistogramwidget.cpp:52
QgsRasterMinMaxWidget::userHasSetManualMinMaxValues
void userHasSetManualMinMaxValues()
Uncheck cumulative cut, min/max, std-dev radio buttons.
Definition: qgsrasterminmaxwidget.cpp:84
QgsRasterRendererWidget::stdDev
virtual QString stdDev()
Definition: qgsrasterrendererwidget.h:73
QgsRasterHistogramWidget::histoAction
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
Definition: qgsrasterhistogramwidget.cpp:762
QgsRasterLayer::bandCount
int bandCount() const
Returns the number of bands in this layer.
Definition: qgsrasterlayer.cpp:217
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:102
QgsRasterHistogram::histogramVector
QgsRasterHistogram::HistogramVector histogramVector
Stores the histogram for a given layer.
Definition: qgsrasterhistogram.h:71
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
Qgis::UInt32
@ UInt32
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:107
QgsRasterDataProvider::dataType
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsRasterInterface::bandStatistics
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Definition: qgsrasterinterface.cpp:116
QgsRasterInterface::sourceDataType
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...
Definition: qgsrasterinterface.h:223
QgsRasterInterface::Size
@ Size
Definition: qgsrasterinterface.h:186
QgsRasterHistogramWidget::apply
void apply() override
Definition: qgsrasterhistogramwidget.cpp:1236
QgsRasterRendererWidget::minMaxWidget
virtual QgsRasterMinMaxWidget * minMaxWidget()
Returns min/max widget when it exists.
Definition: qgsrasterrendererwidget.h:81
QgsContrastEnhancement::maximumValuePossible
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a GDAL data type.
Definition: qgscontrastenhancement.h:66
QgsRasterHistogramWidget::setRendererWidget
void setRendererWidget(const QString &name, QgsRasterRendererWidget *rendererWidget=nullptr)
Sets the renderer widget (or just its name if there is no widget)
Definition: qgsrasterhistogramwidget.cpp:247
QwtDoublePoint
QPointF QwtDoublePoint
Definition: qgscurveeditorwidget.h:36
QgsRasterHistogramWidget::setActive
void setActive(bool activeFlag)
Activate the histogram widget.
Definition: qgsrasterhistogramwidget.cpp:255
QgsRasterInterface::hasHistogram
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)
Definition: qgsrasterinterface.cpp:377
QgsRasterRendererWidget::setMax
virtual void setMax(const QString &value, int index=0)
Definition: qgsrasterrendererwidget.h:72
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
qgsrasterrendererregistry.h
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgsrasterrendererwidget.h
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsRasterLayer::bandName
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
Definition: qgsrasterlayer.cpp:223
QgsMapLayerConfigWidget
A panel widget that can be shown in the map style dock.
Definition: qgsmaplayerconfigwidget.h:34
QgsApplication::getThemePixmap
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.
Definition: qgsapplication.cpp:721
qgsapplication.h
QgsRasterBandStats::maximumValue
double maximumValue
The maximum cell value in the raster band.
Definition: qgsrasterbandstats.h:88
QgsRasterHistogramWidget::computeHistogram
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
Definition: qgsrasterhistogramwidget.cpp:327
QgsRasterHistogramWidget::QgsRasterHistogramWidget
QgsRasterHistogramWidget(QgsRasterLayer *layer, QWidget *parent=nullptr)
Constructor for QgsRasterHistogramWidget, for the specified raster layer.
Definition: qgsrasterhistogramwidget.cpp:54
QgsRasterHistogram::minimum
double minimum
The minimum histogram value.
Definition: qgsrasterhistogram.h:77
QgsRasterInterface::xSize
virtual int xSize() const
Gets raster size.
Definition: qgsrasterinterface.h:241
findClosestTickVal
QString findClosestTickVal(double target, const QwtScaleDiv *scale, int div=100)
Definition: qgsrasterhistogramwidget.cpp:1043
QgsDoubleValidator
QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator and QRegularExpressionVali...
Definition: qgsdoublevalidator.h:39
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:234
QgsRasterHistogramWidget::setSelectedBand
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
Definition: qgsrasterhistogramwidget.cpp:725
QgsRasterBandStats::Min
@ Min
Definition: qgsrasterbandstats.h:40
QgsRasterRendererWidget::min
virtual QString min(int index=0)
Definition: qgsrasterrendererwidget.h:69
QgsContrastEnhancement::minimumValuePossible
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a GDAL data type.
Definition: qgscontrastenhancement.h:105
QgsFeedback::progressChanged
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
QgsRasterHistogram::binCount
int binCount
Number of bins (intervals,buckets) in histogram.
Definition: qgsrasterhistogram.h:60
qgsrasterhistogramwidget.h
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:289
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:71
QgsGuiUtils::getSaveAsImageName
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:90
Qgis::UInt16
@ UInt16
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:105
QgsRasterRendererWidget::selectedBand
virtual int selectedBand(int index=0)
Definition: qgsrasterrendererwidget.h:75
QgsRasterInterface::ySize
virtual int ySize() const
Definition: qgsrasterinterface.h:242
QgsRasterHistogramWidget::histoSaveAsImage
bool histoSaveAsImage(const QString &filename, int width=600, int height=600, int quality=-1)
Save the histogram as an image to disk.
Definition: qgsrasterhistogramwidget.cpp:691
QgsRasterBandStats::minimumValue
double minimumValue
The minimum cell value in the raster band.
Definition: qgsrasterbandstats.h:94
QgsRasterBandStats::Max
@ Max
Definition: qgsrasterbandstats.h:41
QgsRasterDataProvider::sourceDataType
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...
QgsRasterInterface
Base class for processing filters like renderers, reprojector, resampler etc.
Definition: qgsrasterinterface.h:117
QgsRasterInterface::histogram
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.
Definition: qgsrasterinterface.cpp:404
QgsRasterHistogram
The QgsRasterHistogram is a container for histogram of a single raster band.
Definition: qgsrasterhistogram.h:34
qgsrasterminmaxwidget.h
QgsRasterBlockFeedback
Feedback object tailored for raster block reading.
Definition: qgsrasterinterface.h:41
qgssettings.h
Qgis::Int16
@ Int16
Sixteen bit signed integer (qint16)
Definition: qgis.h:106
Qgis::Int32
@ Int32
Thirty two bit signed integer (qint32)
Definition: qgis.h:108
QgsRasterRendererWidget::setMin
virtual void setMin(const QString &value, int index=0)
Definition: qgsrasterrendererwidget.h:71
QgsRasterRendererWidget
Definition: qgsrasterrendererwidget.h:37
qgsdoublevalidator.h
qgsguiutils.h
QgsRasterHistogram::maximum
double maximum
The maximum histogram value.
Definition: qgsrasterhistogram.h:74
QgsRasterRendererWidget::max
virtual QString max(int index=0)
Definition: qgsrasterrendererwidget.h:70
Qgis::Byte
@ Byte
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
QgsRasterInterface::capabilities
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
Definition: qgsrasterinterface.h:206
QgsRasterInterface::extent
virtual QgsRectangle extent() const
Gets the extent of the interface.
Definition: qgsrasterinterface.h:229
QgsRasterHistogramWidget::refreshHistogram
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
Definition: qgsrasterhistogramwidget.cpp:375
qgsrasterdataprovider.h