QGIS API Documentation  2.99.0-Master (e077efd)
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;
121  QAction* action;
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 
248 {
249 }
250 
251 void QgsRasterHistogramWidget::setRendererWidget( const QString& name, QgsRasterRendererWidget* rendererWidget )
252 {
253  mRendererName = name;
254  mRendererWidget = rendererWidget;
256  on_cboHistoBand_currentIndexChanged( -1 );
257 }
258 
259 void QgsRasterHistogramWidget::setActive( bool theActiveFlag )
260 {
261  if ( theActiveFlag )
262  {
264  on_cboHistoBand_currentIndexChanged( -1 );
265  }
266  else
267  {
268  if ( QApplication::overrideCursor() )
269  QApplication::restoreOverrideCursor();
270  btnHistoMin->setChecked( false );
271  btnHistoMax->setChecked( false );
272  }
273 }
274 
275 void QgsRasterHistogramWidget::on_btnHistoCompute_clicked()
276 {
277 // Histogram computation can be called either by clicking the "Compute Histogram" button
278 // which is only visible if there is no cached histogram or by calling the
279 // "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
280 // to re-calculate the histogram if it has already been calculated
281  computeHistogram( true );
283 }
284 
285 bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
286 {
287 
288  //bool myIgnoreOutOfRangeFlag = true;
289  //bool myThoroughBandScanFlag = false;
290  int myBandCountInt = mRasterLayer->bandCount();
291 
292  // if forceComputeFlag = false make sure raster has cached histogram, else return false
293  if ( ! forceComputeFlag )
294  {
295  for ( int myIteratorInt = 1;
296  myIteratorInt <= myBandCountInt;
297  ++myIteratorInt )
298  {
299  int sampleSize = 250000; // number of sample cells
300  if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
301  {
302  QgsDebugMsg( QString( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
303  return false;
304  }
305  }
306  }
307 
308  // compute histogram
309  stackedWidget2->setCurrentIndex( 1 );
310  connect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
311  QApplication::setOverrideCursor( Qt::WaitCursor );
312 
313  for ( int myIteratorInt = 1;
314  myIteratorInt <= myBandCountInt;
315  ++myIteratorInt )
316  {
317  int sampleSize = 250000; // number of sample cells
318  mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
319  }
320 
321  disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
322  // mHistogramProgress->hide();
323  stackedWidget2->setCurrentIndex( 0 );
324  QApplication::restoreOverrideCursor();
325 
326  return true;
327 }
328 
329 
331 {
332  // Explanation:
333  // We use the gdal histogram creation routine is called for each selected
334  // layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
335  // the total number of cells that fit into the range defined by that bin.
336  //
337  // The graph routine below determines the greatest number of pixels in any given
338  // bin in all selected layers, and the min. It then draws a scaled line between min
339  // and max - scaled to image height. 1 line drawn per selected band
340  //
341  int myBandCountInt = mRasterLayer->bandCount();
342 
343 
344  if ( ! computeHistogram( false ) )
345  {
346  QgsDebugMsg( QString( "raster does not have cached histogram" ) );
347  stackedWidget2->setCurrentIndex( 2 );
348  return;
349  }
350 
351  // clear plot
352  mpPlot->detachItems();
353 
354  //ensure all children get removed
355  mpPlot->setAutoDelete( true );
356  mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
357  mpPlot->insertLegend( new QwtLegend(), QwtPlot::BottomLegend );
358  // Set axis titles
359  mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Pixel Value" ) );
360  mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Frequency" ) );
361  mpPlot->setAxisAutoScale( QwtPlot::yLeft );
362 
363  // x axis scale only set after computing global min/max across bands (see below)
364  // add a grid
365  QwtPlotGrid * myGrid = new QwtPlotGrid();
366  myGrid->attach( mpPlot );
367 
368  // make colors list
369  mHistoColors.clear();
370  mHistoColors << Qt::black; // first element, not used
371  QVector<QColor> myColors;
372  myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
373  qsrand( myBandCountInt * 100 ); // make sure colors are always the same for a given band count
374  while ( myColors.size() <= myBandCountInt )
375  {
376  myColors <<
377  QColor( 1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
378  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
379  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ) );
380  }
381  //randomise seed again
382  qsrand( time( nullptr ) );
383 
384  // assign colors to each band, depending on the current RGB/gray band selection
385  // grayscale
386  QList< int > mySelectedBands = rendererSelectedBands();
387  if ( mRendererName == QLatin1String( "singlebandgray" ) )
388  {
389  int myGrayBand = mySelectedBands[0];
390  for ( int i = 1; i <= myBandCountInt; i++ )
391  {
392  if ( i == myGrayBand )
393  {
394  mHistoColors << Qt::darkGray;
395  cboHistoBand->setItemData( i - 1, QColor( Qt::darkGray ), Qt::ForegroundRole );
396  }
397  else
398  {
399  if ( ! myColors.isEmpty() )
400  {
401  mHistoColors << myColors.first();
402  myColors.pop_front();
403  }
404  else
405  {
406  mHistoColors << Qt::black;
407  }
408  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
409  }
410  }
411  }
412  // RGB
413  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
414  {
415  int myRedBand = mySelectedBands[0];
416  int myGreenBand = mySelectedBands[1];
417  int myBlueBand = mySelectedBands[2];
418  // remove RGB, which are reserved for the actual RGB bands
419  // show name of RGB bands in appropriate color in bold
420  myColors.remove( 0, 3 );
421  for ( int i = 1; i <= myBandCountInt; i++ )
422  {
423  QColor myColor;
424  if ( i == myRedBand )
425  myColor = Qt::red;
426  else if ( i == myGreenBand )
427  myColor = Qt::green;
428  else if ( i == myBlueBand )
429  myColor = Qt::blue;
430  else
431  {
432  if ( ! myColors.isEmpty() )
433  {
434  myColor = myColors.first();
435  myColors.pop_front();
436  }
437  else
438  {
439  myColor = Qt::black;
440  }
441  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
442  }
443  if ( i == myRedBand || i == myGreenBand || i == myBlueBand )
444  {
445  cboHistoBand->setItemData( i - 1, myColor, Qt::ForegroundRole );
446  }
447  mHistoColors << myColor;
448  }
449  }
450  else
451  {
452  mHistoColors << myColors;
453  }
454 
455  //
456  //now draw actual graphs
457  //
458 
459  //somtimes there are more bins than needed
460  //we find out the last one that actually has data in it
461  //so we can discard the rest and set the x-axis scales correctly
462  //
463  // scan through to get counts from layers' histograms
464  //
465  mHistoMin = 0;
466  mHistoMax = 0;
467  bool myFirstIteration = true;
468  /* get selected band list, if mHistoShowBands != ShowAll */
469  mySelectedBands = histoSelectedBands();
470  double myBinXStep = 1;
471  double myBinX = 0;
472 
473  for ( int myIteratorInt = 1;
474  myIteratorInt <= myBandCountInt;
475  ++myIteratorInt )
476  {
477  /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
478  if ( mHistoShowBands != ShowAll )
479  {
480  if ( ! mySelectedBands.contains( myIteratorInt ) )
481  continue;
482  }
483 
484  int sampleSize = 250000; // number of sample cells
485  QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
486 
487  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 ) );
488 
489  Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( myIteratorInt );
490  bool myDrawLines = true;
491  if ( ! mHistoDrawLines &&
492  ( mySrcDataType == Qgis::Byte ||
493  mySrcDataType == Qgis::Int16 || mySrcDataType == Qgis::Int32 ||
494  mySrcDataType == Qgis::UInt16 || mySrcDataType == Qgis::UInt32 ) )
495  {
496  myDrawLines = false;
497  }
498 
499  QwtPlotCurve * mypCurve = nullptr;
500  if ( myDrawLines )
501  {
502  mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
503  //mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
504  mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
505  mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
506  }
507 
508 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
509  QwtPlotHistogram * mypHisto = 0;
510  if ( ! myDrawLines )
511  {
512  mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( myIteratorInt ) );
513  mypHisto->setRenderHint( QwtPlotItem::RenderAntialiased );
514  //mypHisto->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
515  mypHisto->setPen( QPen( Qt::lightGray ) );
516  // this is needed in order to see the colors in the legend
517  mypHisto->setBrush( QBrush( mHistoColors.at( myIteratorInt ) ) );
518  }
519 #else
520  HistogramItem *mypHistoItem = nullptr;
521  if ( ! myDrawLines )
522  {
523  mypHistoItem = new HistogramItem( tr( "Band %1" ).arg( myIteratorInt ) );
524  mypHistoItem->setRenderHint( QwtPlotItem::RenderAntialiased );
525  mypHistoItem->setColor( mHistoColors.at( myIteratorInt ) );
526  }
527 #endif
528 
529 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
530  QVector<QPointF> data;
531  QVector<QwtIntervalSample> dataHisto;
532 #else
533  QVector<double> myX2Data;
534  QVector<double> myY2Data;
535  // we safely assume that QT>=4.0 (min version is 4.7), therefore QwtArray is a QVector, so don't set size here
536  QwtArray<QwtDoubleInterval> intervalsHisto;
537  QwtArray<double> valuesHisto;
538 
539 #endif
540 
541  // calculate first bin x value and bin step size if not Byte data
542  if ( mySrcDataType != Qgis::Byte )
543  {
544  myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
545  myBinX = myHistogram.minimum + myBinXStep / 2.0;
546  }
547  else
548  {
549  myBinXStep = 1;
550  myBinX = 0;
551  }
552 
553  for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
554  {
555  int myBinValue = myHistogram.histogramVector.at( myBin );
556 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
557  if ( myDrawLines )
558  {
559  data << QPointF( myBinX, myBinValue );
560  }
561  else
562  {
563  dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
564  }
565 #else
566  if ( myDrawLines )
567  {
568  myX2Data.append( double( myBinX ) );
569  myY2Data.append( double( myBinValue ) );
570  }
571  else
572  {
573  intervalsHisto.append( QwtDoubleInterval( myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 ) );
574  valuesHisto.append( double( myBinValue ) );
575  }
576 #endif
577  myBinX += myBinXStep;
578  }
579 
580 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
581  if ( myDrawLines )
582  {
583  mypCurve->setSamples( data );
584  mypCurve->attach( mpPlot );
585  }
586  else
587  {
588  mypHisto->setSamples( dataHisto );
589  mypHisto->attach( mpPlot );
590  }
591 #else
592  if ( myDrawLines )
593  {
594  mypCurve->setData( myX2Data, myY2Data );
595  mypCurve->attach( mpPlot );
596  }
597  else
598  {
599  mypHistoItem->setData( QwtIntervalData( intervalsHisto, valuesHisto ) );
600  mypHistoItem->attach( mpPlot );
601  }
602 #endif
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( QString( "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 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
644  mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
645  connect( mHistoPicker, SIGNAL( selected( const QPointF & ) ), this, SLOT( histoPickerSelected( const QPointF & ) ) );
646 #else
647  mHistoPicker->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::DragSelection );
648  connect( mHistoPicker, SIGNAL( selected( const QwtDoublePoint & ) ), this, SLOT( histoPickerSelectedQwt5( const QwtDoublePoint & ) ) );
649 #endif
650  }
651  mHistoPicker->setEnabled( false );
652 
653  // plot zoomer
654  if ( !mHistoZoomer )
655  {
656  mHistoZoomer = new QwtPlotZoomer( mpPlot->canvas() );
657 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
658  mHistoZoomer->setStateMachine( new QwtPickerDragRectMachine );
659 #else
660  mHistoZoomer->setSelectionFlags( QwtPicker::RectSelection | QwtPicker::DragSelection );
661 #endif
662  mHistoZoomer->setTrackerMode( QwtPicker::AlwaysOff );
663  }
664  mHistoZoomer->setEnabled( true );
665  }
666  else
667  {
668  mpPlot->setDisabled( true );
669  if ( mHistoPicker )
670  mHistoPicker->setEnabled( false );
671  if ( mHistoZoomer )
672  mHistoZoomer->setEnabled( false );
673  }
674 
675  disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
676  stackedWidget2->setCurrentIndex( 0 );
677  // icon from http://findicons.com/icon/169577/14_zoom?id=171427
678  mpPlot->canvas()->setCursor( QCursor( QgsApplication::getThemePixmap( QStringLiteral( "/mIconZoom.svg" ) ) ) );
679  // on_cboHistoBand_currentIndexChanged( -1 );
680  QApplication::restoreOverrideCursor();
681 }
682 
684 {
685  if ( !mpPlot )
686  return;
687 
688  QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
689  QFileInfo myInfo( myFileNameAndFilter.first );
690  if ( myInfo.baseName() != QLatin1String( "" ) )
691  {
692  histoSaveAsImage( myFileNameAndFilter.first );
693  }
694 }
695 
696 bool QgsRasterHistogramWidget::histoSaveAsImage( const QString& theFilename,
697  int width, int height, int quality )
698 {
699  // make sure dir. exists
700  QFileInfo myInfo( theFilename );
701  QDir myDir( myInfo.dir() );
702  if ( ! myDir.exists() )
703  {
704  QgsDebugMsg( QString( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath(), theFilename ) );
705  return false;
706  }
707 
708  // prepare the pixmap
709  QPixmap myPixmap( width, height );
710  QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
711  myPixmap.fill( Qt::white ); // Qt::transparent ?
712 
713 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
714  QwtPlotRenderer myRenderer;
715  myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
716  QwtPlotRenderer::DiscardCanvasBackground );
717  myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
718 
719  QPainter myPainter;
720  myPainter.begin( &myPixmap );
721  myRenderer.render( mpPlot, &myPainter, myQRect );
722  myPainter.end();
723 #else
724  QwtPlotPrintFilter myFilter;
725  int myOptions = QwtPlotPrintFilter::PrintAll;
726  myOptions &= ~QwtPlotPrintFilter::PrintBackground;
727  myOptions |= QwtPlotPrintFilter::PrintFrameWithScales;
728  myFilter.setOptions( myOptions );
729 
730  QPainter myPainter;
731  myPainter.begin( &myPixmap );
732  mpPlot->print( &myPainter, myQRect, myFilter );
733  myPainter.end();
734 
735  // "fix" for bug in qwt5 - legend and plot shifts a bit
736  // can't see how to avoid this without picking qwt5 apart...
739 #endif
740 
741  // save pixmap to file
742  myPixmap.save( theFilename, nullptr, quality );
743 
744  // should do more error checking
745  return true;
746 }
747 
749 {
750  cboHistoBand->setCurrentIndex( theBandNo - 1 );
751 }
752 
753 void QgsRasterHistogramWidget::on_cboHistoBand_currentIndexChanged( int index )
754 {
755  if ( mHistoShowBands == ShowSelected )
757 
758  // get the current index value, index can be -1
759  index = cboHistoBand->currentIndex();
760  if ( mHistoPicker )
761  {
762  mHistoPicker->setEnabled( false );
763  mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
764  }
765  if ( mHistoZoomer )
766  mHistoZoomer->setEnabled( true );
767  btnHistoMin->setEnabled( true );
768  btnHistoMax->setEnabled( true );
769 
770  QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
771  leHistoMin->setText( myMinMax.first );
772  leHistoMax->setText( myMinMax.second );
773 
774  applyHistoMin();
775  applyHistoMax();
776 }
777 
778 void QgsRasterHistogramWidget::histoActionTriggered( QAction* action )
779 {
780  if ( ! action )
781  return;
782  histoAction( action->data().toString(), action->isChecked() );
783 }
784 
785 void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
786 {
787  if ( actionName == QLatin1String( "" ) )
788  return;
789 
790  // this approach is a bit of a hack, but this way we don't have to define slots for each action
791  QgsDebugMsg( QString( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
792 
793  // checkeable actions
794  if ( actionName == QLatin1String( "Show markers" ) )
795  {
796  mHistoShowMarkers = actionFlag;
797  QSettings settings;
798  settings.setValue( QStringLiteral( "/Raster/histogram/showMarkers" ), mHistoShowMarkers );
799  updateHistoMarkers();
800  return;
801  }
802  else if ( actionName == QLatin1String( "Zoom min_max" ) )
803  {
804  mHistoZoomToMinMax = actionFlag;
805  QSettings settings;
806  settings.setValue( QStringLiteral( "/Raster/histogram/zoomToMinMax" ), mHistoZoomToMinMax );
807  return;
808  }
809  else if ( actionName == QLatin1String( "Update min_max" ) )
810  {
811  mHistoUpdateStyleToMinMax = actionFlag;
812  QSettings settings;
813  settings.setValue( QStringLiteral( "/Raster/histogram/updateStyleToMinMax" ), mHistoUpdateStyleToMinMax );
814  return;
815  }
816  else if ( actionName == QLatin1String( "Show all" ) )
817  {
818  mHistoShowBands = ShowAll;
819  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
821  return;
822  }
823  else if ( actionName == QLatin1String( "Show selected" ) )
824  {
825  mHistoShowBands = ShowSelected;
826  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
828  return;
829  }
830  else if ( actionName == QLatin1String( "Show RGB" ) )
831  {
832  mHistoShowBands = ShowRGB;
833  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
835  return;
836  }
837  else if ( actionName == QLatin1String( "Draw lines" ) )
838  {
839  mHistoDrawLines = actionFlag;
840  QSettings settings;
841  settings.setValue( QStringLiteral( "/Raster/histogram/drawLines" ), mHistoDrawLines );
842  on_btnHistoCompute_clicked(); // refresh
843  return;
844  }
845 #if 0
846  else if ( actionName == "Load apply all" )
847  {
848  mHistoLoadApplyAll = actionFlag;
849  settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
850  return;
851  }
852 #endif
853  // Load actions
854  // TODO - separate calculations from rendererwidget so we can do them without
855  else if ( actionName.left( 5 ) == QLatin1String( "Load " ) && mRendererWidget )
856  {
857  QVector<int> myBands;
858  bool ok = false;
859 
860 #if 0
861  double minMaxValues[2];
862 
863  // find which band(s) need updating (all or current)
864  if ( mHistoLoadApplyAll )
865  {
866  int myBandCountInt = mRasterLayer->bandCount();
867  for ( int i = 1; i <= myBandCountInt; i++ )
868  {
869  if ( i != cboHistoBand->currentIndex() + 1 )
870  myBands << i;
871  }
872  }
873 #endif
874 
875  // add current band to the end
876  myBands << cboHistoBand->currentIndex() + 1;
877 
878  // get stddev value once if needed
879 #if 0
880  double myStdDev = 1.0;
881  if ( actionName == "Load stddev" )
882  {
883  myStdDev = mRendererWidget->stdDev().toDouble();
884  }
885 #endif
886 
887  // don't update markers every time
888  leHistoMin->blockSignals( true );
889  leHistoMax->blockSignals( true );
890 
891  // process each band
892  Q_FOREACH ( int theBandNo, myBands )
893  {
894  ok = false;
895 #if 0
896  if ( actionName == "Load actual" )
897  {
898  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
899  theBandNo, minMaxValues );
900  }
901  else if ( actionName == "Load estimate" )
902  {
903  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
904  theBandNo, minMaxValues );
905  }
906  else if ( actionName == "Load extent" )
907  {
908  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
909  theBandNo, minMaxValues );
910  }
911  else if ( actionName == "Load 1 stddev" ||
912  actionName == "Load stddev" )
913  {
914  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, theBandNo, minMaxValues );
915  }
916 #endif
917 
918  // apply current item
919  cboHistoBand->setCurrentIndex( theBandNo - 1 );
920  if ( !ok || actionName == QLatin1String( "Load reset" ) )
921  {
922  leHistoMin->clear();
923  leHistoMax->clear();
924 #if 0
925  // TODO - fix gdal provider: changes data type when nodata value is not found
926  // this prevents us from getting proper min and max values here
928  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
930  ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
931  }
932  else
933  {
934  leHistoMin->setText( QString::number( minMaxValues[0] ) );
935  leHistoMax->setText( QString::number( minMaxValues[1] ) );
936 #endif
937  }
938  applyHistoMin();
939  applyHistoMax();
940  }
941  // update markers
942  leHistoMin->blockSignals( false );
943  leHistoMax->blockSignals( false );
944  updateHistoMarkers();
945  }
946  else if ( actionName == QLatin1String( "Compute histogram" ) )
947  {
948  on_btnHistoCompute_clicked();
949  }
950  else
951  {
952  QgsDebugMsg( "Invalid action " + actionName );
953  return;
954  }
955 }
956 
957 void QgsRasterHistogramWidget::applyHistoMin()
958 {
959  if ( ! mRendererWidget )
960  return;
961 
962  int theBandNo = cboHistoBand->currentIndex() + 1;
963  QList< int > mySelectedBands = rendererSelectedBands();
964  QString min;
965  for ( int i = 0; i <= mySelectedBands.size(); i++ )
966  {
967  if ( theBandNo == mRendererWidget->selectedBand( i ) )
968  {
969  min = leHistoMin->text();
970  if ( mHistoUpdateStyleToMinMax )
971  mRendererWidget->setMin( min, i );
972  }
973  }
974 
975  updateHistoMarkers();
976 
977  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
978  {
979  QRectF rect = mHistoZoomer->zoomRect();
980  rect.setLeft( min.toDouble() );
981  mHistoZoomer->zoom( rect );
982  }
983  emit widgetChanged();
984 }
985 
986 void QgsRasterHistogramWidget::applyHistoMax()
987 {
988  if ( ! mRendererWidget )
989  return;
990 
991  int theBandNo = cboHistoBand->currentIndex() + 1;
992  QList< int > mySelectedBands = rendererSelectedBands();
993  QString max;
994  for ( int i = 0; i <= mySelectedBands.size(); i++ )
995  {
996  if ( theBandNo == mRendererWidget->selectedBand( i ) )
997  {
998  max = leHistoMax->text();
999  if ( mHistoUpdateStyleToMinMax )
1000  mRendererWidget->setMax( max, i );
1001  }
1002  }
1003 
1004  updateHistoMarkers();
1005 
1006  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
1007  {
1008  QRectF rect = mHistoZoomer->zoomRect();
1009  rect.setRight( max.toDouble() );
1010  mHistoZoomer->zoom( rect );
1011  }
1012  emit widgetChanged();
1013 }
1014 
1015 void QgsRasterHistogramWidget::on_btnHistoMin_toggled()
1016 {
1017  if ( mpPlot && mHistoPicker )
1018  {
1019  if ( QApplication::overrideCursor() )
1020  QApplication::restoreOverrideCursor();
1021  if ( btnHistoMin->isChecked() )
1022  {
1023  btnHistoMax->setChecked( false );
1024  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1025  }
1026  if ( mHistoZoomer )
1027  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
1028  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1029  }
1030  updateHistoMarkers();
1031 }
1032 
1033 void QgsRasterHistogramWidget::on_btnHistoMax_toggled()
1034 {
1035  if ( mpPlot && mHistoPicker )
1036  {
1037  if ( QApplication::overrideCursor() )
1038  QApplication::restoreOverrideCursor();
1039  if ( btnHistoMax->isChecked() )
1040  {
1041  btnHistoMin->setChecked( false );
1042  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1043  }
1044  if ( mHistoZoomer )
1045  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
1046  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1047  }
1048  updateHistoMarkers();
1049 }
1050 
1051 // local function used by histoPickerSelected(), to get a rounded picked value
1052 // this is sensitive and may not always be correct, needs more testing
1053 QString findClosestTickVal( double target, const QwtScaleDiv * scale, int div = 100 )
1054 {
1055  if ( !scale ) return QLatin1String( "" );
1056 
1057  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1058  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1059  double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1060  double min = majorTicks[0] - diff;
1061  if ( min > target )
1062  min -= ( majorTicks[1] - majorTicks[0] );
1063 #if defined(QWT_VERSION) && QWT_VERSION<0x050200
1064  double max = scale->hBound();
1065 #else
1066  double max = scale->upperBound();
1067 #endif
1068  double closest = target;
1069  double current = min;
1070 
1071  while ( current < max )
1072  {
1073  current += diff;
1074  if ( current > target )
1075  {
1076  closest = ( qAbs( target - current + diff ) < qAbs( target - current ) ) ? current - diff : current;
1077  break;
1078  }
1079  }
1080 
1081  // QgsDebugMsg( QString( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1082  return QString::number( closest );
1083 }
1084 
1085 void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1086 {
1087  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1088  {
1089 #if defined(QWT_VERSION) && QWT_VERSION>=0x060100
1090  const QwtScaleDiv * scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1091 #else
1092  const QwtScaleDiv * scale = mpPlot->axisScaleDiv( QwtPlot::xBottom );
1093 #endif
1094 
1095  if ( btnHistoMin->isChecked() )
1096  {
1097  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1098  applyHistoMin();
1099  btnHistoMin->setChecked( false );
1100  }
1101  else // if ( btnHistoMax->isChecked() )
1102  {
1103  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1104  applyHistoMax();
1105  btnHistoMax->setChecked( false );
1106  }
1107  }
1108  if ( QApplication::overrideCursor() )
1109  QApplication::restoreOverrideCursor();
1110 }
1111 
1112 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1113 {
1114  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1115 }
1116 
1117 void QgsRasterHistogramWidget::updateHistoMarkers()
1118 {
1119  // hack to not update markers
1120  if ( leHistoMin->signalsBlocked() )
1121  return;
1122  // todo error checking
1123  if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1124  return;
1125 
1126  int theBandNo = cboHistoBand->currentIndex() + 1;
1127  QList< int > mySelectedBands = histoSelectedBands();
1128 
1129  if (( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1130  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( theBandNo ) ) )
1131  {
1132  mHistoMarkerMin->hide();
1133  mHistoMarkerMax->hide();
1134  mpPlot->replot();
1135  return;
1136  }
1137 
1138  double minVal = mHistoMin;
1139  double maxVal = mHistoMax;
1140  QString minStr = leHistoMin->text();
1141  QString maxStr = leHistoMax->text();
1142  if ( minStr != QLatin1String( "" ) )
1143  minVal = minStr.toDouble();
1144  if ( maxStr != QLatin1String( "" ) )
1145  maxVal = maxStr.toDouble();
1146 
1147  QPen linePen = QPen( mHistoColors.at( theBandNo ) );
1148  linePen.setStyle( Qt::DashLine );
1149  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1150  mHistoMarkerMin->setLinePen( linePen );
1151  mHistoMarkerMin->setXValue( minVal );
1152  mHistoMarkerMin->show();
1153  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1154  mHistoMarkerMax->setLinePen( linePen );
1155  mHistoMarkerMax->setXValue( maxVal );
1156  mHistoMarkerMax->show();
1157 
1158  mpPlot->replot();
1159 }
1160 
1161 
1162 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1163 {
1164  QList< int > mySelectedBands;
1165 
1166  if ( mHistoShowBands != ShowAll )
1167  {
1168  if ( mHistoShowBands == ShowSelected )
1169  {
1170  mySelectedBands << cboHistoBand->currentIndex() + 1;
1171  }
1172  else if ( mHistoShowBands == ShowRGB )
1173  {
1174  mySelectedBands = rendererSelectedBands();
1175  }
1176  }
1177 
1178  return mySelectedBands;
1179 }
1180 
1181 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1182 {
1183  QList< int > mySelectedBands;
1184 
1185  if ( ! mRendererWidget )
1186  {
1187  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1188  return mySelectedBands;
1189  }
1190 
1191  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1192  {
1193  mySelectedBands << mRendererWidget->selectedBand();
1194  }
1195  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1196  {
1197  for ( int i = 0; i <= 2; i++ )
1198  {
1199  mySelectedBands << mRendererWidget->selectedBand( i );
1200  }
1201  }
1202 
1203  return mySelectedBands;
1204 }
1205 
1206 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int theBandNo )
1207 {
1208  QPair< QString, QString > myMinMax;
1209 
1210  if ( ! mRendererWidget )
1211  return myMinMax;
1212 
1213  if ( mRendererName == QLatin1String( "singlebandgray" ) )
1214  {
1215  if ( theBandNo == mRendererWidget->selectedBand() )
1216  {
1217  myMinMax.first = mRendererWidget->min();
1218  myMinMax.second = mRendererWidget->max();
1219  }
1220  }
1221  else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1222  {
1223  for ( int i = 0; i <= 2; i++ )
1224  {
1225  if ( theBandNo == mRendererWidget->selectedBand( i ) )
1226  {
1227  myMinMax.first = mRendererWidget->min( i );
1228  myMinMax.second = mRendererWidget->max( i );
1229  break;
1230  }
1231  }
1232  }
1233 
1234  // TODO - there are 2 definitions of raster data type that should be unified
1235  // QgsRasterDataProvider::DataType and Qgis::DataType
1236  // TODO - fix gdal provider: changes data type when nodata value is not found
1237  // this prevents us from getting proper min and max values here
1238  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1239  // mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
1240  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1241  // mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
1242 
1243  // if we get an empty result, fill with default value (histo min/max)
1244  if ( myMinMax.first.isEmpty() )
1245  myMinMax.first = QString::number( mHistoMin );
1246  if ( myMinMax.second.isEmpty() )
1247  myMinMax.second = QString::number( mHistoMax );
1248 
1249  QgsDebugMsg( QString( "bandNo %1 got min/max [%2] [%3]" ).arg( theBandNo ).arg( myMinMax.first, myMinMax.second ) );
1250 
1251  return myMinMax;
1252 }
1253 
1255 {
1256 
1257 }
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:35
Thirty two bit signed integer (qint32)
Definition: qgis.h:67
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:33
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:66
DataType
Raster data types.
Definition: qgis.h:60
QString bandName(int theBandNoInt) const
Get the name of a band given its number.
Sixteen bit signed integer (qint16)
Definition: qgis.h:65
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:64
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:86
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:63