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