QGIS API Documentation  2.14.0-Essen
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  : QWidget( 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  QgsDebugMsg( "entered." );
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 );
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  QgsDebugMsg( "entered." );
344 
345  if ( ! computeHistogram( false ) )
346  {
347  QgsDebugMsg( QString( "raster does not have cached histogram" ) );
348  stackedWidget2->setCurrentIndex( 2 );
349  return;
350  }
351 
352  // clear plot
353  mpPlot->detachItems();
354 
355  //ensure all children get removed
356  mpPlot->setAutoDelete( true );
357  mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
358  mpPlot->insertLegend( new QwtLegend(), QwtPlot::BottomLegend );
359  // Set axis titles
360  mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Pixel Value" ) );
361  mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Frequency" ) );
362  mpPlot->setAxisAutoScale( QwtPlot::yLeft );
363 
364  // x axis scale only set after computing global min/max across bands (see below)
365  // add a grid
366  QwtPlotGrid * myGrid = new QwtPlotGrid();
367  myGrid->attach( mpPlot );
368 
369  // make colors list
370  mHistoColors.clear();
371  mHistoColors << Qt::black; // first element, not used
372  QVector<QColor> myColors;
373  myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
374  qsrand( myBandCountInt * 100 ); // make sure colors are always the same for a given band count
375  while ( myColors.size() <= myBandCountInt )
376  {
377  myColors <<
378  QColor( 1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
379  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
380  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ) );
381  }
382  //randomise seed again
383  qsrand( time( nullptr ) );
384 
385  // assign colors to each band, depending on the current RGB/gray band selection
386  // grayscale
387  QList< int > mySelectedBands = rendererSelectedBands();
388  if ( mRendererName == "singlebandgray" )
389  {
390  int myGrayBand = mySelectedBands[0];
391  for ( int i = 1; i <= myBandCountInt; i++ )
392  {
393  if ( i == myGrayBand )
394  {
395  mHistoColors << Qt::darkGray;
396  cboHistoBand->setItemData( i - 1, QColor( Qt::darkGray ), Qt::ForegroundRole );
397  }
398  else
399  {
400  if ( ! myColors.isEmpty() )
401  {
402  mHistoColors << myColors.first();
403  myColors.pop_front();
404  }
405  else
406  {
407  mHistoColors << Qt::black;
408  }
409  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
410  }
411  }
412  }
413  // RGB
414  else if ( mRendererName == "multibandcolor" )
415  {
416  int myRedBand = mySelectedBands[0];
417  int myGreenBand = mySelectedBands[1];
418  int myBlueBand = mySelectedBands[2];
419  // remove RGB, which are reserved for the actual RGB bands
420  // show name of RGB bands in appropriate color in bold
421  myColors.remove( 0, 3 );
422  for ( int i = 1; i <= myBandCountInt; i++ )
423  {
424  QColor myColor;
425  if ( i == myRedBand )
426  myColor = Qt::red;
427  else if ( i == myGreenBand )
428  myColor = Qt::green;
429  else if ( i == myBlueBand )
430  myColor = Qt::blue;
431  else
432  {
433  if ( ! myColors.isEmpty() )
434  {
435  myColor = myColors.first();
436  myColors.pop_front();
437  }
438  else
439  {
440  myColor = Qt::black;
441  }
442  cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
443  }
444  if ( i == myRedBand || i == myGreenBand || i == myBlueBand )
445  {
446  cboHistoBand->setItemData( i - 1, myColor, Qt::ForegroundRole );
447  }
448  mHistoColors << myColor;
449  }
450  }
451  else
452  {
453  mHistoColors << myColors;
454  }
455 
456  //
457  //now draw actual graphs
458  //
459 
460  //somtimes there are more bins than needed
461  //we find out the last one that actually has data in it
462  //so we can discard the rest and set the x-axis scales correctly
463  //
464  // scan through to get counts from layers' histograms
465  //
466  mHistoMin = 0;
467  mHistoMax = 0;
468  bool myFirstIteration = true;
469  /* get selected band list, if mHistoShowBands != ShowAll */
470  mySelectedBands = histoSelectedBands();
471  double myBinXStep = 1;
472  double myBinX = 0;
473 
474  for ( int myIteratorInt = 1;
475  myIteratorInt <= myBandCountInt;
476  ++myIteratorInt )
477  {
478  /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
479  if ( mHistoShowBands != ShowAll )
480  {
481  if ( ! mySelectedBands.contains( myIteratorInt ) )
482  continue;
483  }
484 
485  int sampleSize = 250000; // number of sample cells
486  QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
487 
488  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 ) );
489 
490  QGis::DataType mySrcDataType = mRasterLayer->dataProvider()->srcDataType( myIteratorInt );
491  bool myDrawLines = true;
492  if ( ! mHistoDrawLines &&
493  ( mySrcDataType == QGis::Byte ||
494  mySrcDataType == QGis::Int16 || mySrcDataType == QGis::Int32 ||
495  mySrcDataType == QGis::UInt16 || mySrcDataType == QGis::UInt32 ) )
496  {
497  myDrawLines = false;
498  }
499 
500  QwtPlotCurve * mypCurve = nullptr;
501  if ( myDrawLines )
502  {
503  mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( myIteratorInt ) );
504  //mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
505  mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
506  mypCurve->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
507  }
508 
509 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
510  QwtPlotHistogram * mypHisto = 0;
511  if ( ! myDrawLines )
512  {
513  mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( myIteratorInt ) );
514  mypHisto->setRenderHint( QwtPlotItem::RenderAntialiased );
515  //mypHisto->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
516  mypHisto->setPen( QPen( Qt::lightGray ) );
517  // this is needed in order to see the colors in the legend
518  mypHisto->setBrush( QBrush( mHistoColors.at( myIteratorInt ) ) );
519  }
520 #else
521  HistogramItem *mypHistoItem = nullptr;
522  if ( ! myDrawLines )
523  {
524  mypHistoItem = new HistogramItem( tr( "Band %1" ).arg( myIteratorInt ) );
525  mypHistoItem->setRenderHint( QwtPlotItem::RenderAntialiased );
526  mypHistoItem->setColor( mHistoColors.at( myIteratorInt ) );
527  }
528 #endif
529 
530 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
531  QVector<QPointF> data;
532  QVector<QwtIntervalSample> dataHisto;
533 #else
534  QVector<double> myX2Data;
535  QVector<double> myY2Data;
536  // we safely assume that QT>=4.0 (min version is 4.7), therefore QwtArray is a QVector, so don't set size here
537  QwtArray<QwtDoubleInterval> intervalsHisto;
538  QwtArray<double> valuesHisto;
539 
540 #endif
541 
542  // calculate first bin x value and bin step size if not Byte data
543  if ( mySrcDataType != QGis::Byte )
544  {
545  myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
546  myBinX = myHistogram.minimum + myBinXStep / 2.0;
547  }
548  else
549  {
550  myBinXStep = 1;
551  myBinX = 0;
552  }
553 
554  for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
555  {
556  int myBinValue = myHistogram.histogramVector.at( myBin );
557 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
558  if ( myDrawLines )
559  {
560  data << QPointF( myBinX, myBinValue );
561  }
562  else
563  {
564  dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
565  }
566 #else
567  if ( myDrawLines )
568  {
569  myX2Data.append( double( myBinX ) );
570  myY2Data.append( double( myBinValue ) );
571  }
572  else
573  {
574  intervalsHisto.append( QwtDoubleInterval( myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 ) );
575  valuesHisto.append( double( myBinValue ) );
576  }
577 #endif
578  myBinX += myBinXStep;
579  }
580 
581 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
582  if ( myDrawLines )
583  {
584  mypCurve->setSamples( data );
585  mypCurve->attach( mpPlot );
586  }
587  else
588  {
589  mypHisto->setSamples( dataHisto );
590  mypHisto->attach( mpPlot );
591  }
592 #else
593  if ( myDrawLines )
594  {
595  mypCurve->setData( myX2Data, myY2Data );
596  mypCurve->attach( mpPlot );
597  }
598  else
599  {
600  mypHistoItem->setData( QwtIntervalData( intervalsHisto, valuesHisto ) );
601  mypHistoItem->attach( mpPlot );
602  }
603 #endif
604 
605  if ( myFirstIteration || mHistoMin > myHistogram.minimum )
606  {
607  mHistoMin = myHistogram.minimum;
608  }
609  if ( myFirstIteration || mHistoMax < myHistogram.maximum )
610  {
611  mHistoMax = myHistogram.maximum;
612  }
613  QgsDebugMsg( QString( "computed histo min = %1 max = %2" ).arg( mHistoMin ).arg( mHistoMax ) );
614  myFirstIteration = false;
615  }
616 
617  if ( mHistoMin < mHistoMax )
618  {
619  // for x axis use band pixel values rather than gdal hist. bin values
620  // subtract -0.5 to prevent rounding errors
621  // see http://www.gdal.org/classGDALRasterBand.html#3f8889607d3b2294f7e0f11181c201c8
622  // fix x range for non-Byte data
623  mpPlot->setAxisScale( QwtPlot::xBottom,
624  mHistoMin - myBinXStep / 2,
625  mHistoMax + myBinXStep / 2 );
626  mpPlot->setEnabled( true );
627  mpPlot->replot();
628 
629  // histo plot markers
630  // memory leak?
631  mHistoMarkerMin = new QwtPlotMarker();
632  mHistoMarkerMin->attach( mpPlot );
633  mHistoMarkerMax = new QwtPlotMarker();
634  mHistoMarkerMax->attach( mpPlot );
635  updateHistoMarkers();
636 
637  // histo picker
638  if ( !mHistoPicker )
639  {
640  mHistoPicker = new QwtPlotPicker( mpPlot->canvas() );
641  // mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
642  mHistoPicker->setTrackerMode( QwtPicker::AlwaysOff );
643  mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
644 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
645  mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
646  connect( mHistoPicker, SIGNAL( selected( const QPointF & ) ), this, SLOT( histoPickerSelected( const QPointF & ) ) );
647 #else
648  mHistoPicker->setSelectionFlags( QwtPicker::PointSelection | QwtPicker::DragSelection );
649  connect( mHistoPicker, SIGNAL( selected( const QwtDoublePoint & ) ), this, SLOT( histoPickerSelectedQwt5( const QwtDoublePoint & ) ) );
650 #endif
651  }
652  mHistoPicker->setEnabled( false );
653 
654  // plot zoomer
655  if ( !mHistoZoomer )
656  {
657  mHistoZoomer = new QwtPlotZoomer( mpPlot->canvas() );
658 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
659  mHistoZoomer->setStateMachine( new QwtPickerDragRectMachine );
660 #else
661  mHistoZoomer->setSelectionFlags( QwtPicker::RectSelection | QwtPicker::DragSelection );
662 #endif
663  mHistoZoomer->setTrackerMode( QwtPicker::AlwaysOff );
664  }
665  mHistoZoomer->setEnabled( true );
666  }
667  else
668  {
669  mpPlot->setDisabled( true );
670  if ( mHistoPicker )
671  mHistoPicker->setEnabled( false );
672  if ( mHistoZoomer )
673  mHistoZoomer->setEnabled( false );
674  }
675 
676  disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
677  stackedWidget2->setCurrentIndex( 0 );
678  // icon from http://findicons.com/icon/169577/14_zoom?id=171427
679  mpPlot->canvas()->setCursor( QCursor( QgsApplication::getThemePixmap( "/mIconZoom.svg" ) ) );
680  // on_cboHistoBand_currentIndexChanged( -1 );
682 }
683 
685 {
686  if ( !mpPlot )
687  return;
688 
689  QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
690  QFileInfo myInfo( myFileNameAndFilter.first );
691  if ( QFileInfo( myFileNameAndFilter.first ).baseName() != "" )
692  {
693  histoSaveAsImage( myFileNameAndFilter.first );
694  }
695 }
696 
698  int width, int height, int quality )
699 {
700  // make sure dir. exists
701  QFileInfo myInfo( theFilename );
702  QDir myDir( myInfo.dir() );
703  if ( ! myDir.exists() )
704  {
705  QgsDebugMsg( QString( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath(), theFilename ) );
706  return false;
707  }
708 
709  // prepare the pixmap
710  QPixmap myPixmap( width, height );
711  QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
712  myPixmap.fill( Qt::white ); // Qt::transparent ?
713 
714 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
715  QwtPlotRenderer myRenderer;
716  myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
717  QwtPlotRenderer::DiscardCanvasBackground );
718  myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
719 
720  QPainter myPainter;
721  myPainter.begin( &myPixmap );
722  myRenderer.render( mpPlot, &myPainter, myQRect );
723  myPainter.end();
724 #else
725  QwtPlotPrintFilter myFilter;
726  int myOptions = QwtPlotPrintFilter::PrintAll;
727  myOptions &= ~QwtPlotPrintFilter::PrintBackground;
728  myOptions |= QwtPlotPrintFilter::PrintFrameWithScales;
729  myFilter.setOptions( myOptions );
730 
731  QPainter myPainter;
732  myPainter.begin( &myPixmap );
733  mpPlot->print( &myPainter, myQRect, myFilter );
734  myPainter.end();
735 
736  // "fix" for bug in qwt5 - legend and plot shifts a bit
737  // can't see how to avoid this without picking qwt5 apart...
740 #endif
741 
742  // save pixmap to file
743  myPixmap.save( theFilename, nullptr, quality );
744 
745  // should do more error checking
746  return true;
747 }
748 
750 {
751  cboHistoBand->setCurrentIndex( theBandNo - 1 );
752 }
753 
754 void QgsRasterHistogramWidget::on_cboHistoBand_currentIndexChanged( int index )
755 {
756  if ( mHistoShowBands == ShowSelected )
758 
759  // get the current index value, index can be -1
760  index = cboHistoBand->currentIndex();
761  if ( mHistoPicker )
762  {
763  mHistoPicker->setEnabled( false );
764  mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
765  }
766  if ( mHistoZoomer )
767  mHistoZoomer->setEnabled( true );
768  btnHistoMin->setEnabled( true );
769  btnHistoMax->setEnabled( true );
770 
771  QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
772  leHistoMin->setText( myMinMax.first );
773  leHistoMax->setText( myMinMax.second );
774 
775  applyHistoMin();
776  applyHistoMax();
777 }
778 
779 void QgsRasterHistogramWidget::histoActionTriggered( QAction* action )
780 {
781  if ( ! action )
782  return;
783  histoAction( action->data().toString(), action->isChecked() );
784 }
785 
786 void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
787 {
788  if ( actionName == "" )
789  return;
790 
791  // this approach is a bit of a hack, but this way we don't have to define slots for each action
792  QgsDebugMsg( QString( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
793 
794  // checkeable actions
795  if ( actionName == "Show markers" )
796  {
797  mHistoShowMarkers = actionFlag;
798  QSettings settings;
799  settings.setValue( "/Raster/histogram/showMarkers", mHistoShowMarkers );
800  updateHistoMarkers();
801  return;
802  }
803  else if ( actionName == "Zoom min_max" )
804  {
805  mHistoZoomToMinMax = actionFlag;
806  QSettings settings;
807  settings.setValue( "/Raster/histogram/zoomToMinMax", mHistoZoomToMinMax );
808  return;
809  }
810  else if ( actionName == "Update min_max" )
811  {
812  mHistoUpdateStyleToMinMax = actionFlag;
813  QSettings settings;
814  settings.setValue( "/Raster/histogram/updateStyleToMinMax", mHistoUpdateStyleToMinMax );
815  return;
816  }
817  else if ( actionName == "Show all" )
818  {
819  mHistoShowBands = ShowAll;
820  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
822  return;
823  }
824  else if ( actionName == "Show selected" )
825  {
826  mHistoShowBands = ShowSelected;
827  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
829  return;
830  }
831  else if ( actionName == "Show RGB" )
832  {
833  mHistoShowBands = ShowRGB;
834  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
836  return;
837  }
838  else if ( actionName == "Draw lines" )
839  {
840  mHistoDrawLines = actionFlag;
841  QSettings settings;
842  settings.setValue( "/Raster/histogram/drawLines", mHistoDrawLines );
843  on_btnHistoCompute_clicked(); // refresh
844  return;
845  }
846 #if 0
847  else if ( actionName == "Load apply all" )
848  {
849  mHistoLoadApplyAll = actionFlag;
850  settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
851  return;
852  }
853 #endif
854  // Load actions
855  // TODO - separate calculations from rendererwidget so we can do them without
856  else if ( actionName.left( 5 ) == "Load " && mRendererWidget )
857  {
858  QVector<int> myBands;
859  bool ok = false;
860 
861 #if 0
862  double minMaxValues[2];
863 
864  // find which band(s) need updating (all or current)
865  if ( mHistoLoadApplyAll )
866  {
867  int myBandCountInt = mRasterLayer->bandCount();
868  for ( int i = 1; i <= myBandCountInt; i++ )
869  {
870  if ( i != cboHistoBand->currentIndex() + 1 )
871  myBands << i;
872  }
873  }
874 #endif
875 
876  // add current band to the end
877  myBands << cboHistoBand->currentIndex() + 1;
878 
879  // get stddev value once if needed
880 #if 0
881  double myStdDev = 1.0;
882  if ( actionName == "Load stddev" )
883  {
884  myStdDev = mRendererWidget->stdDev().toDouble();
885  }
886 #endif
887 
888  // don't update markers every time
889  leHistoMin->blockSignals( true );
890  leHistoMax->blockSignals( true );
891 
892  // process each band
893  Q_FOREACH ( int theBandNo, myBands )
894  {
895  ok = false;
896 #if 0
897  if ( actionName == "Load actual" )
898  {
899  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
900  theBandNo, minMaxValues );
901  }
902  else if ( actionName == "Load estimate" )
903  {
904  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
905  theBandNo, minMaxValues );
906  }
907  else if ( actionName == "Load extent" )
908  {
909  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
910  theBandNo, minMaxValues );
911  }
912  else if ( actionName == "Load 1 stddev" ||
913  actionName == "Load stddev" )
914  {
915  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, theBandNo, minMaxValues );
916  }
917 #endif
918 
919  // apply current item
920  cboHistoBand->setCurrentIndex( theBandNo - 1 );
921  if ( !ok || actionName == "Load reset" )
922  {
923  leHistoMin->clear();
924  leHistoMax->clear();
925 #if 0
926  // TODO - fix gdal provider: changes data type when nodata value is not found
927  // this prevents us from getting proper min and max values here
929  ( QGis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
931  ( QGis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
932  }
933  else
934  {
935  leHistoMin->setText( QString::number( minMaxValues[0] ) );
936  leHistoMax->setText( QString::number( minMaxValues[1] ) );
937 #endif
938  }
939  applyHistoMin();
940  applyHistoMax();
941  }
942  // update markers
943  leHistoMin->blockSignals( false );
944  leHistoMax->blockSignals( false );
945  updateHistoMarkers();
946  }
947  else if ( actionName == "Compute histogram" )
948  {
949  on_btnHistoCompute_clicked();
950  }
951  else
952  {
953  QgsDebugMsg( "Invalid action " + actionName );
954  return;
955  }
956 }
957 
958 void QgsRasterHistogramWidget::applyHistoMin()
959 {
960  if ( ! mRendererWidget )
961  return;
962 
963  int theBandNo = cboHistoBand->currentIndex() + 1;
964  QList< int > mySelectedBands = rendererSelectedBands();
965  QString min;
966  for ( int i = 0; i <= mySelectedBands.size(); i++ )
967  {
968  if ( theBandNo == mRendererWidget->selectedBand( i ) )
969  {
970  min = leHistoMin->text();
971  if ( mHistoUpdateStyleToMinMax )
972  mRendererWidget->setMin( min, i );
973  }
974  }
975 
976  updateHistoMarkers();
977 
978  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
979  {
980  QRectF rect = mHistoZoomer->zoomRect();
981  rect.setLeft( min.toDouble() );
982  mHistoZoomer->zoom( rect );
983  }
984 
985 }
986 
987 void QgsRasterHistogramWidget::applyHistoMax()
988 {
989  if ( ! mRendererWidget )
990  return;
991 
992  int theBandNo = cboHistoBand->currentIndex() + 1;
993  QList< int > mySelectedBands = rendererSelectedBands();
994  QString max;
995  for ( int i = 0; i <= mySelectedBands.size(); i++ )
996  {
997  if ( theBandNo == mRendererWidget->selectedBand( i ) )
998  {
999  max = leHistoMax->text();
1000  if ( mHistoUpdateStyleToMinMax )
1001  mRendererWidget->setMax( max, i );
1002  }
1003  }
1004 
1005  updateHistoMarkers();
1006 
1007  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
1008  {
1009  QRectF rect = mHistoZoomer->zoomRect();
1010  rect.setRight( max.toDouble() );
1011  mHistoZoomer->zoom( rect );
1012  }
1013 }
1014 
1015 void QgsRasterHistogramWidget::on_btnHistoMin_toggled()
1016 {
1017  if ( mpPlot && mHistoPicker )
1018  {
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  {
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 "";
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  }
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 != "" )
1143  minVal = minStr.toDouble();
1144  if ( maxStr != "" )
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 == "singlebandgray" )
1192  {
1193  mySelectedBands << mRendererWidget->selectedBand();
1194  }
1195  else if ( mRendererName == "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 == "singlebandgray" )
1214  {
1215  if ( theBandNo == mRendererWidget->selectedBand() )
1216  {
1217  myMinMax.first = mRendererWidget->min();
1218  myMinMax.second = mRendererWidget->max();
1219  }
1220  }
1221  else if ( mRendererName == "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 }
void setText(const QString &text)
Eight bit unsigned integer (quint8)
Definition: qgis.h:132
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)
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:133
int bandCount() const
Get the number of bands in this layer.
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:136
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:135
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
Sixteen bit signed integer (qint16)
Definition: qgis.h:134
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:129
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()