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