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