QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 > selectedBands = rendererSelectedBands();
937  QString min;
938  for ( int i = 0; i <= selectedBands.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 );
947  {
949  }
950  if ( mRendererWidget->minMaxWidget() )
951  {
952  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
953  }
954  }
955  }
956  }
957 
958  updateHistoMarkers();
959 
960  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
961  {
962  QRectF rect = mHistoZoomer->zoomRect();
963  rect.setLeft( min.toDouble() );
964  mHistoZoomer->zoom( rect );
965  }
966  emit widgetChanged();
967 }
968 
969 void QgsRasterHistogramWidget::applyHistoMax()
970 {
971  if ( ! mRendererWidget )
972  return;
973 
974  const int bandNo = cboHistoBand->currentIndex() + 1;
975  const QList< int > mySelectedBands = rendererSelectedBands();
976  QString max;
977  for ( int i = 0; i <= mySelectedBands.size(); i++ )
978  {
979  if ( bandNo == mRendererWidget->selectedBand( i ) )
980  {
981  max = leHistoMax->text();
982  if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
983  {
984  mRendererWidget->setMax( max, i );
986  {
988  }
989  if ( mRendererWidget->minMaxWidget() )
990  {
991  mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
992  }
993  }
994  }
995  }
996 
997  updateHistoMarkers();
998 
999  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
1000  {
1001  QRectF rect = mHistoZoomer->zoomRect();
1002  rect.setRight( max.toDouble() );
1003  mHistoZoomer->zoom( rect );
1004  }
1005  emit widgetChanged();
1006 }
1007 
1008 void QgsRasterHistogramWidget::btnHistoMin_toggled()
1009 {
1010  if ( mpPlot && mHistoPicker )
1011  {
1012  if ( QApplication::overrideCursor() )
1013  QApplication::restoreOverrideCursor();
1014  if ( btnHistoMin->isChecked() )
1015  {
1016  btnHistoMax->setChecked( false );
1017  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1018  }
1019  if ( mHistoZoomer )
1020  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
1021  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1022  }
1023  updateHistoMarkers();
1024 }
1025 
1026 void QgsRasterHistogramWidget::btnHistoMax_toggled()
1027 {
1028  if ( mpPlot && mHistoPicker )
1029  {
1030  if ( QApplication::overrideCursor() )
1031  QApplication::restoreOverrideCursor();
1032  if ( btnHistoMax->isChecked() )
1033  {
1034  btnHistoMin->setChecked( false );
1035  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1036  }
1037  if ( mHistoZoomer )
1038  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
1039  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1040  }
1041  updateHistoMarkers();
1042 }
1043 
1044 // local function used by histoPickerSelected(), to get a rounded picked value
1045 // this is sensitive and may not always be correct, needs more testing
1046 QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
1047 {
1048  if ( !scale ) return QString();
1049 
1050  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1051  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1052  const double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1053  double min = majorTicks[0] - diff;
1054  if ( min > target )
1055  min -= ( majorTicks[1] - majorTicks[0] );
1056  const double max = scale->upperBound();
1057  double closest = target;
1058  double current = min;
1059 
1060  while ( current < max )
1061  {
1062  current += diff;
1063  if ( current > target )
1064  {
1065  closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current;
1066  break;
1067  }
1068  }
1069 
1070  // QgsDebugMsg( QStringLiteral( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1071  return QLocale().toString( closest );
1072 }
1073 
1074 void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1075 {
1076  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1077  {
1078  const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1079 
1080  if ( btnHistoMin->isChecked() )
1081  {
1082  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1083  applyHistoMin();
1084  btnHistoMin->setChecked( false );
1085  }
1086  else // if ( btnHistoMax->isChecked() )
1087  {
1088  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1089  applyHistoMax();
1090  btnHistoMax->setChecked( false );
1091  }
1092  }
1093  if ( QApplication::overrideCursor() )
1094  QApplication::restoreOverrideCursor();
1095 }
1096 
1097 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1098 {
1099  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1100 }
1101 
1102 void QgsRasterHistogramWidget::updateHistoMarkers()
1103 {
1104  // hack to not update markers
1105  if ( leHistoMin->signalsBlocked() )
1106  return;
1107  // todo error checking
1108  if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1109  return;
1110 
1111  const int bandNo = cboHistoBand->currentIndex() + 1;
1112  const QList< int > mySelectedBands = histoSelectedBands();
1113 
1114  if ( ( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1115  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( bandNo ) ) )
1116  {
1117  mHistoMarkerMin->hide();
1118  mHistoMarkerMax->hide();
1119  mpPlot->replot();
1120  return;
1121  }
1122 
1123  double minVal = mHistoMin;
1124  double maxVal = mHistoMax;
1125  const QString minStr = leHistoMin->text();
1126  const QString maxStr = leHistoMax->text();
1127  if ( !minStr.isEmpty() )
1128  minVal = minStr.toDouble();
1129  if ( !maxStr.isEmpty() )
1130  maxVal = maxStr.toDouble();
1131 
1132  QPen linePen = QPen( mHistoColors.at( bandNo ) );
1133  linePen.setStyle( Qt::DashLine );
1134  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1135  mHistoMarkerMin->setLinePen( linePen );
1136  mHistoMarkerMin->setXValue( minVal );
1137  mHistoMarkerMin->show();
1138  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1139  mHistoMarkerMax->setLinePen( linePen );
1140  mHistoMarkerMax->setXValue( maxVal );
1141  mHistoMarkerMax->show();
1142 
1143  mpPlot->replot();
1144 }
1145 
1146 
1147 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1148 {
1149  QList< int > mySelectedBands;
1150 
1151  if ( mHistoShowBands != ShowAll )
1152  {
1153  if ( mHistoShowBands == ShowSelected )
1154  {
1155  mySelectedBands << cboHistoBand->currentIndex() + 1;
1156  }
1157  else if ( mHistoShowBands == ShowRGB )
1158  {
1159  mySelectedBands = rendererSelectedBands();
1160  }
1161  }
1162 
1163  return mySelectedBands;
1164 }
1165 
1166 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1167 {
1168  QList< int > mySelectedBands;
1169 
1170  if ( ! mRendererWidget )
1171  {
1172  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1173  return mySelectedBands;
1174  }
1175 
1176  if ( mRendererName == QLatin1String( "singlebandgray" ) ||
1177  mRendererName == QLatin1String( "singlebandpseudocolor" ) )
1178  {
1179  mySelectedBands << mRendererWidget->selectedBand();
1180  }
1181  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1182  {
1183  for ( int i = 0; i <= 2; i++ )
1184  {
1185  mySelectedBands << mRendererWidget->selectedBand( i );
1186  }
1187  }
1188 
1189  return mySelectedBands;
1190 }
1191 
1192 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1193 {
1194  QPair< QString, QString > myMinMax;
1195 
1196  if ( ! mRendererWidget )
1197  return myMinMax;
1198 
1199  if ( mRendererName == QLatin1String( "singlebandgray" ) ||
1200  mRendererName == QLatin1String( "singlebandpseudocolor" ) )
1201  {
1202  if ( bandNo == mRendererWidget->selectedBand() )
1203  {
1204  myMinMax.first = mRendererWidget->min();
1205  myMinMax.second = mRendererWidget->max();
1206  }
1207  }
1208  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1209  {
1210  for ( int i = 0; i <= 2; i++ )
1211  {
1212  if ( bandNo == mRendererWidget->selectedBand( i ) )
1213  {
1214  myMinMax.first = mRendererWidget->min( i );
1215  myMinMax.second = mRendererWidget->max( i );
1216  break;
1217  }
1218  }
1219  }
1220 
1221  // TODO - there are 2 definitions of raster data type that should be unified
1222  // QgsRasterDataProvider::DataType and Qgis::DataType
1223  // TODO - fix gdal provider: changes data type when nodata value is not found
1224  // this prevents us from getting proper min and max values here
1225  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1226  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1227  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1228  // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1229 
1230  // if we get an empty result, fill with default value (histo min/max)
1231  if ( myMinMax.first.isEmpty() )
1232  myMinMax.first = QLocale().toString( mHistoMin );
1233  if ( myMinMax.second.isEmpty() )
1234  myMinMax.second = QLocale().toString( mHistoMax );
1235 
1236  QgsDebugMsg( QStringLiteral( "bandNo %1 got min/max [%2] [%3]" ).arg( bandNo ).arg( myMinMax.first, myMinMax.second ) );
1237 
1238  return myMinMax;
1239 }
1240 
1242 {
1243 
1244 }
SAMPLE_SIZE
constexpr int SAMPLE_SIZE
Definition: qgsrasterhistogramwidget.cpp:55
QgsRasterMinMaxWidget::userHasSetManualMinMaxValues
void userHasSetManualMinMaxValues()
Uncheck cumulative cut, min/max, std-dev radio buttons.
Definition: qgsrasterminmaxwidget.cpp:89
QgsRasterRendererWidget::stdDev
virtual QString stdDev()
Definition: qgsrasterrendererwidget.h:92
QgsRasterHistogramWidget::histoAction
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
Definition: qgsrasterhistogramwidget.cpp:757
QgsRasterLayer::bandCount
int bandCount() const
Returns the number of bands in this layer.
Definition: qgsrasterlayer.cpp:240
QgsRasterHistogram::histogramVector
QgsRasterHistogram::HistogramVector histogramVector
Stores the histogram for a given layer.
Definition: qgsrasterhistogram.h:84
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:161
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:242
Qgis::DataType::UInt32
@ UInt32
Thirty two bit unsigned integer (quint32)
QgsRasterInterface::Size
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Definition: qgsrasterinterface.h:205
QgsRasterHistogramWidget::apply
void apply() override
Definition: qgsrasterhistogramwidget.cpp:1241
QgsRasterRendererWidget::minMaxWidget
virtual QgsRasterMinMaxWidget * minMaxWidget()
Returns min/max widget when it exists.
Definition: qgsrasterrendererwidget.h:100
QgsContrastEnhancement::maximumValuePossible
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a data type.
Definition: qgscontrastenhancement.h:69
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:250
QwtDoublePoint
QPointF QwtDoublePoint
Definition: qgscurveeditorwidget.h:36
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:128
Qgis::DataType::Byte
@ Byte
Eight bit unsigned integer (quint8)
QgsRasterHistogramWidget::setActive
void setActive(bool activeFlag)
Activate the histogram widget.
Definition: qgsrasterhistogramwidget.cpp:258
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:91
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
Qgis::DataType::Int32
@ Int32
Thirty two bit signed integer (qint32)
qgsrasterrendererregistry.h
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgsrasterrendererwidget.h
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsRasterLayer::bandName
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
Definition: qgsrasterlayer.cpp:246
QgsMapLayerConfigWidget
A panel widget that can be shown in the map style dock.
Definition: qgsmaplayerconfigwidget.h:115
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:827
qgsapplication.h
QgsRasterBandStats::maximumValue
double maximumValue
The maximum cell value in the raster band.
Definition: qgsrasterbandstats.h:101
QgsRasterHistogramWidget::computeHistogram
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
Definition: qgsrasterhistogramwidget.cpp:330
QgsRasterHistogramWidget::QgsRasterHistogramWidget
QgsRasterHistogramWidget(QgsRasterLayer *layer, QWidget *parent=nullptr)
Constructor for QgsRasterHistogramWidget, for the specified raster layer.
Definition: qgsrasterhistogramwidget.cpp:57
QgsRasterHistogram::minimum
double minimum
The minimum histogram value.
Definition: qgsrasterhistogram.h:90
QgsRasterInterface::xSize
virtual int xSize() const
Gets raster size.
Definition: qgsrasterinterface.h:263
QgsContrastEnhancement::StretchToMinimumMaximum
@ StretchToMinimumMaximum
Linear histogram.
Definition: qgscontrastenhancement.h:51
findClosestTickVal
QString findClosestTickVal(double target, const QwtScaleDiv *scale, int div=100)
Definition: qgsrasterhistogramwidget.cpp:1046
QgsContrastEnhancement::NoEnhancement
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
Definition: qgscontrastenhancement.h:50
QgsRasterRendererWidget::contrastEnhancementAlgorithm
virtual QgsContrastEnhancement::ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
Returns the constrast enhancement algorithm to be used by the raster renderer.
Definition: qgsrasterrendererwidget.h:106
QgsDoubleValidator
QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator and QRegularExpressionVali...
Definition: qgsdoublevalidator.h:40
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
Qgis::DataType::Int16
@ Int16
Sixteen bit signed integer (qint16)
QgsRasterHistogramWidget::setSelectedBand
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
Definition: qgsrasterhistogramwidget.cpp:720
QgsRasterBandStats::Min
@ Min
Definition: qgsrasterbandstats.h:66
QgsRasterRendererWidget::min
virtual QString min(int index=0)
Definition: qgsrasterrendererwidget.h:88
QgsContrastEnhancement::minimumValuePossible
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a data type.
Definition: qgscontrastenhancement.h:108
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:73
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:279
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:76
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:104
Qgis::DataType::UInt16
@ UInt16
Sixteen bit unsigned integer (quint16)
QgsRasterRendererWidget::selectedBand
virtual int selectedBand(int index=0)
Definition: qgsrasterrendererwidget.h:94
QgsRasterInterface::ySize
virtual int ySize() const
Definition: qgsrasterinterface.h:264
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:686
QgsRasterBandStats::minimumValue
double minimumValue
The minimum cell value in the raster band.
Definition: qgsrasterbandstats.h:107
QgsRasterBandStats::Max
@ Max
Definition: qgsrasterbandstats.h:67
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:135
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. It is used to cache comp...
Definition: qgsrasterhistogram.h:33
qgsrasterminmaxwidget.h
QgsRasterBlockFeedback
Feedback object tailored for raster block reading.
Definition: qgsrasterinterface.h:41
qgssettings.h
QgsRasterRendererWidget::setMin
virtual void setMin(const QString &value, int index=0)
Definition: qgsrasterrendererwidget.h:90
QgsRasterRendererWidget::setContrastEnhancementAlgorithm
virtual void setContrastEnhancementAlgorithm(QgsContrastEnhancement::ContrastEnhancementAlgorithm algorithm)
Sets the constrast enhancement algorithm to be used by the raster renderer.
Definition: qgsrasterrendererwidget.h:112
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsRasterRendererWidget
Abstract base class for widgets which configure a QgsRasterRenderer.
Definition: qgsrasterrendererwidget.h:39
qgsdoublevalidator.h
qgsguiutils.h
QgsRasterHistogram::maximum
double maximum
The maximum histogram value.
Definition: qgsrasterhistogram.h:87
QgsRasterRendererWidget::max
virtual QString max(int index=0)
Definition: qgsrasterrendererwidget.h:89
QgsRasterInterface::capabilities
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
Definition: qgsrasterinterface.h:225
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:257
QgsRasterInterface::extent
virtual QgsRectangle extent() const
Gets the extent of the interface.
Definition: qgsrasterinterface.h:248
QgsRasterHistogramWidget::refreshHistogram
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
Definition: qgsrasterhistogramwidget.cpp:378
qgsrasterdataprovider.h