QGIS API Documentation  2.14.11-Essen
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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)
void setActive(bool theActiveFlag)
Activate the histogram widget.
Sixteen bit signed integer (qint16)
Definition: qgis.h:134
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)
Eight bit unsigned integer (quint8)
Definition: qgis.h:132
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:133
#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
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)
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...
bool isEmpty() const
bool isEmpty() const
void remove(int i)
virtual int selectedBand(int index=0)
void setOverrideCursor(const QCursor &cursor)
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
DataType
Raster data types.
Definition: qgis.h:129
QgsRasterHistogramWidget(QgsRasterLayer *lyr, QWidget *parent=nullptr)
QRect rect() const
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
Thirty two bit signed integer (qint32)
Definition: qgis.h:136
bool isEmpty() const
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
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)
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:135
QCursor * overrideCursor()