QGIS API Documentation  2.9.0-Master
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 WIN32
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( 0 )
61 {
62  setupUi( this );
63 
64  mSaveAsImageButton->setIcon( QgsApplication::getThemeIcon( "/mActionFileSave.svg" ) );
65 
66  mRendererWidget = 0;
67  mRendererName = "singlebandgray";
68 
69  mHistoMin = 0;
70  mHistoMax = 0;
71 
72  mHistoPicker = NULL;
73  mHistoZoomer = NULL;
74  mHistoMarkerMin = NULL;
75  mHistoMarkerMax = NULL;
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 
250 void QgsRasterHistogramWidget::setRendererWidget( const QString& name, QgsRasterRendererWidget* rendererWidget )
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  {
267  if ( QApplication::overrideCursor() )
268  QApplication::restoreOverrideCursor();
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 );
324  QApplication::restoreOverrideCursor();
325 
326  return true;
327 }
328 
329 
331 {
332  // Explanation:
333  // We use the gdal histogram creation routine is called for each selected
334  // layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
335  // the total number of cells that fit into the range defined by that bin.
336  //
337  // The graph routine below determines the greatest number of pixels in any given
338  // bin in all selected layers, and the min. It then draws a scaled line between min
339  // and max - scaled to image height. 1 line drawn per selected band
340  //
341  int myBandCountInt = mRasterLayer->bandCount();
342 
343  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( NULL ) );
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 = 0;
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 = 0;
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 );
681  QApplication::restoreOverrideCursor();
682 }
683 
685 {
686  if ( mpPlot == 0 )
687  {
688  return;
689  }
690 
691  QPair< QString, QString> myFileNameAndFilter = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
692  QFileInfo myInfo( myFileNameAndFilter.first );
693  if ( QFileInfo( myFileNameAndFilter.first ).baseName() != "" )
694  {
695  histoSaveAsImage( myFileNameAndFilter.first );
696  }
697 }
698 
699 bool QgsRasterHistogramWidget::histoSaveAsImage( const QString& theFilename,
700  int width, int height, int quality )
701 {
702  // make sure dir. exists
703  QFileInfo myInfo( theFilename );
704  QDir myDir( myInfo.dir() );
705  if ( ! myDir.exists() )
706  {
707  QgsDebugMsg( QString( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath() ).arg( theFilename ) );
708  return false;
709  }
710 
711  // prepare the pixmap
712  QPixmap myPixmap( width, height );
713  QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
714  myPixmap.fill( Qt::white ); // Qt::transparent ?
715 
716 #if defined(QWT_VERSION) && QWT_VERSION>=0x060000
717  QwtPlotRenderer myRenderer;
718  myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
719  QwtPlotRenderer::DiscardCanvasBackground );
720  myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
721 
722  QPainter myPainter;
723  myPainter.begin( &myPixmap );
724  myRenderer.render( mpPlot, &myPainter, myQRect );
725  myPainter.end();
726 #else
727  QwtPlotPrintFilter myFilter;
728  int myOptions = QwtPlotPrintFilter::PrintAll;
729  myOptions &= ~QwtPlotPrintFilter::PrintBackground;
730  myOptions |= QwtPlotPrintFilter::PrintFrameWithScales;
731  myFilter.setOptions( myOptions );
732 
733  QPainter myPainter;
734  myPainter.begin( &myPixmap );
735  mpPlot->print( &myPainter, myQRect, myFilter );
736  myPainter.end();
737 
738  // "fix" for bug in qwt5 - legend and plot shifts a bit
739  // can't see how to avoid this without picking qwt5 apart...
742 #endif
743 
744  // save pixmap to file
745  myPixmap.save( theFilename, 0, quality );
746 
747  // should do more error checking
748  return true;
749 }
750 
752 {
753  cboHistoBand->setCurrentIndex( theBandNo - 1 );
754 }
755 
756 void QgsRasterHistogramWidget::on_cboHistoBand_currentIndexChanged( int index )
757 {
758  if ( mHistoShowBands == ShowSelected )
760 
761  // get the current index value, index can be -1
762  index = cboHistoBand->currentIndex();
763  if ( mHistoPicker != NULL )
764  {
765  mHistoPicker->setEnabled( false );
766  mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
767  }
768  if ( mHistoZoomer != NULL )
769  mHistoZoomer->setEnabled( true );
770  btnHistoMin->setEnabled( true );
771  btnHistoMax->setEnabled( true );
772 
773  QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
774  leHistoMin->setText( myMinMax.first );
775  leHistoMax->setText( myMinMax.second );
776 
777  applyHistoMin();
778  applyHistoMax();
779 }
780 
781 void QgsRasterHistogramWidget::histoActionTriggered( QAction* action )
782 {
783  if ( ! action )
784  return;
785  histoAction( action->data().toString(), action->isChecked() );
786 }
787 
788 void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
789 {
790  if ( actionName == "" )
791  return;
792 
793  // this approach is a bit of a hack, but this way we don't have to define slots for each action
794  QgsDebugMsg( QString( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ) );
795 
796  // checkeable actions
797  if ( actionName == "Show markers" )
798  {
799  mHistoShowMarkers = actionFlag;
800  QSettings settings;
801  settings.setValue( "/Raster/histogram/showMarkers", mHistoShowMarkers );
802  updateHistoMarkers();
803  return;
804  }
805  else if ( actionName == "Zoom min_max" )
806  {
807  mHistoZoomToMinMax = actionFlag;
808  QSettings settings;
809  settings.setValue( "/Raster/histogram/zoomToMinMax", mHistoZoomToMinMax );
810  return;
811  }
812  else if ( actionName == "Update min_max" )
813  {
814  mHistoUpdateStyleToMinMax = actionFlag;
815  QSettings settings;
816  settings.setValue( "/Raster/histogram/updateStyleToMinMax", mHistoUpdateStyleToMinMax );
817  return;
818  }
819  else if ( actionName == "Show all" )
820  {
821  mHistoShowBands = ShowAll;
822  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
824  return;
825  }
826  else if ( actionName == "Show selected" )
827  {
828  mHistoShowBands = ShowSelected;
829  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
831  return;
832  }
833  else if ( actionName == "Show RGB" )
834  {
835  mHistoShowBands = ShowRGB;
836  // settings.setValue( "/Raster/histogram/showBands", (int)mHistoShowBands );
838  return;
839  }
840  else if ( actionName == "Draw lines" )
841  {
842  mHistoDrawLines = actionFlag;
843  QSettings settings;
844  settings.setValue( "/Raster/histogram/drawLines", mHistoDrawLines );
845  on_btnHistoCompute_clicked(); // refresh
846  return;
847  }
848 #if 0
849  else if ( actionName == "Load apply all" )
850  {
851  mHistoLoadApplyAll = actionFlag;
852  settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
853  return;
854  }
855 #endif
856  // Load actions
857  // TODO - separate calculations from rendererwidget so we can do them without
858  else if ( actionName.left( 5 ) == "Load " && mRendererWidget )
859  {
860  QVector<int> myBands;
861  bool ok = false;
862 
863 #if 0
864  double minMaxValues[2];
865 
866  // find which band(s) need updating (all or current)
867  if ( mHistoLoadApplyAll )
868  {
869  int myBandCountInt = mRasterLayer->bandCount();
870  for ( int i = 1; i <= myBandCountInt; i++ )
871  {
872  if ( i != cboHistoBand->currentIndex() + 1 )
873  myBands << i;
874  }
875  }
876 #endif
877 
878  // add current band to the end
879  myBands << cboHistoBand->currentIndex() + 1;
880 
881  // get stddev value once if needed
882  /*
883  double myStdDev = 1.0;
884  if ( actionName == "Load stddev" )
885  {
886  myStdDev = mRendererWidget->stdDev().toDouble();
887  }
888  */
889 
890  // don't update markers every time
891  leHistoMin->blockSignals( true );
892  leHistoMax->blockSignals( true );
893 
894  // process each band
895  foreach ( int theBandNo, myBands )
896  {
897  ok = false;
898 #if 0
899  if ( actionName == "Load actual" )
900  {
901  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
902  theBandNo, minMaxValues );
903  }
904  else if ( actionName == "Load estimate" )
905  {
906  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
907  theBandNo, minMaxValues );
908  }
909  else if ( actionName == "Load extent" )
910  {
911  ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
912  theBandNo, minMaxValues );
913  }
914  else if ( actionName == "Load 1 stddev" ||
915  actionName == "Load stddev" )
916  {
917  ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, theBandNo, minMaxValues );
918  }
919 #endif
920 
921  // apply current item
922  cboHistoBand->setCurrentIndex( theBandNo - 1 );
923  if ( !ok || actionName == "Load reset" )
924  {
925  leHistoMin->clear();
926  leHistoMax->clear();
927 #if 0
928  // TODO - fix gdal provider: changes data type when nodata value is not found
929  // this prevents us from getting proper min and max values here
931  ( QGis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
933  ( QGis::DataType ) mRasterLayer->dataProvider()->dataType( theBandNo ) );
934  }
935  else
936  {
937  leHistoMin->setText( QString::number( minMaxValues[0] ) );
938  leHistoMax->setText( QString::number( minMaxValues[1] ) );
939 #endif
940  }
941  applyHistoMin();
942  applyHistoMax();
943  }
944  // update markers
945  leHistoMin->blockSignals( false );
946  leHistoMax->blockSignals( false );
947  updateHistoMarkers();
948  }
949  else if ( actionName == "Compute histogram" )
950  {
951  on_btnHistoCompute_clicked();
952  }
953  else
954  {
955  QgsDebugMsg( "Invalid action " + actionName );
956  return;
957  }
958 }
959 
960 void QgsRasterHistogramWidget::applyHistoMin()
961 {
962  if ( ! mRendererWidget )
963  return;
964 
965  int theBandNo = cboHistoBand->currentIndex() + 1;
966  QList< int > mySelectedBands = rendererSelectedBands();
967  QString min;
968  for ( int i = 0; i <= mySelectedBands.size(); i++ )
969  {
970  if ( theBandNo == mRendererWidget->selectedBand( i ) )
971  {
972  min = leHistoMin->text();
973  if ( mHistoUpdateStyleToMinMax )
974  mRendererWidget->setMin( min, i );
975  }
976  }
977 
978  updateHistoMarkers();
979 
980  if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
981  {
982  QRectF rect = mHistoZoomer->zoomRect();
983  rect.setLeft( min.toDouble() );
984  mHistoZoomer->zoom( rect );
985  }
986 
987 }
988 
989 void QgsRasterHistogramWidget::applyHistoMax()
990 {
991  if ( ! mRendererWidget )
992  return;
993 
994  int theBandNo = cboHistoBand->currentIndex() + 1;
995  QList< int > mySelectedBands = rendererSelectedBands();
996  QString max;
997  for ( int i = 0; i <= mySelectedBands.size(); i++ )
998  {
999  if ( theBandNo == mRendererWidget->selectedBand( i ) )
1000  {
1001  max = leHistoMax->text();
1002  if ( mHistoUpdateStyleToMinMax )
1003  mRendererWidget->setMax( max, i );
1004  }
1005  }
1006 
1007  updateHistoMarkers();
1008 
1009  if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
1010  {
1011  QRectF rect = mHistoZoomer->zoomRect();
1012  rect.setRight( max.toDouble() );
1013  mHistoZoomer->zoom( rect );
1014  }
1015 }
1016 
1017 void QgsRasterHistogramWidget::on_btnHistoMin_toggled()
1018 {
1019  if ( mpPlot != NULL && mHistoPicker != NULL )
1020  {
1021  if ( QApplication::overrideCursor() )
1022  QApplication::restoreOverrideCursor();
1023  if ( btnHistoMin->isChecked() )
1024  {
1025  btnHistoMax->setChecked( false );
1026  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1027  }
1028  if ( mHistoZoomer != NULL )
1029  mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
1030  mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1031  }
1032  updateHistoMarkers();
1033 }
1034 
1035 void QgsRasterHistogramWidget::on_btnHistoMax_toggled()
1036 {
1037  if ( mpPlot != NULL && mHistoPicker != NULL )
1038  {
1039  if ( QApplication::overrideCursor() )
1040  QApplication::restoreOverrideCursor();
1041  if ( btnHistoMax->isChecked() )
1042  {
1043  btnHistoMin->setChecked( false );
1044  QApplication::setOverrideCursor( Qt::PointingHandCursor );
1045  }
1046  if ( mHistoZoomer != NULL )
1047  mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
1048  mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1049  }
1050  updateHistoMarkers();
1051 }
1052 
1053 // local function used by histoPickerSelected(), to get a rounded picked value
1054 // this is sensitive and may not always be correct, needs more testing
1055 QString findClosestTickVal( double target, const QwtScaleDiv * scale, int div = 100 )
1056 {
1057  if ( !scale ) return "";
1058 
1059  QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1060  QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1061  double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1062  double min = majorTicks[0] - diff;
1063  if ( min > target )
1064  min -= ( majorTicks[1] - majorTicks[0] );
1065 #if defined(QWT_VERSION) && QWT_VERSION<0x050200
1066  double max = scale->hBound();
1067 #else
1068  double max = scale->upperBound();
1069 #endif
1070  double closest = target;
1071  double current = min;
1072 
1073  while ( current < max )
1074  {
1075  current += diff;
1076  if ( current > target )
1077  {
1078  closest = ( qAbs( target - current + diff ) < qAbs( target - current ) ) ? current - diff : current;
1079  break;
1080  }
1081  }
1082 
1083  // QgsDebugMsg( QString( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ) );
1084  return QString::number( closest );
1085 }
1086 
1087 void QgsRasterHistogramWidget::histoPickerSelected( const QPointF & pos )
1088 {
1089  if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1090  {
1091 #if defined(QWT_VERSION) && QWT_VERSION>=0x060100
1092  const QwtScaleDiv * scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1093 #else
1094  const QwtScaleDiv * scale = mpPlot->axisScaleDiv( QwtPlot::xBottom );
1095 #endif
1096 
1097  if ( btnHistoMin->isChecked() )
1098  {
1099  leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1100  applyHistoMin();
1101  btnHistoMin->setChecked( false );
1102  }
1103  else // if ( btnHistoMax->isChecked() )
1104  {
1105  leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1106  applyHistoMax();
1107  btnHistoMax->setChecked( false );
1108  }
1109  }
1110  if ( QApplication::overrideCursor() )
1111  QApplication::restoreOverrideCursor();
1112 }
1113 
1114 void QgsRasterHistogramWidget::histoPickerSelectedQwt5( const QwtDoublePoint & pos )
1115 {
1116  histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1117 }
1118 
1119 void QgsRasterHistogramWidget::updateHistoMarkers()
1120 {
1121  // hack to not update markers
1122  if ( leHistoMin->signalsBlocked() )
1123  return;
1124  // todo error checking
1125  if ( mpPlot == NULL || mHistoMarkerMin == NULL || mHistoMarkerMax == NULL )
1126  return;
1127 
1128  int theBandNo = cboHistoBand->currentIndex() + 1;
1129  QList< int > mySelectedBands = histoSelectedBands();
1130 
1131  if (( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1132  ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( theBandNo ) ) )
1133  {
1134  mHistoMarkerMin->hide();
1135  mHistoMarkerMax->hide();
1136  mpPlot->replot();
1137  return;
1138  }
1139 
1140  double minVal = mHistoMin;
1141  double maxVal = mHistoMax;
1142  QString minStr = leHistoMin->text();
1143  QString maxStr = leHistoMax->text();
1144  if ( minStr != "" )
1145  minVal = minStr.toDouble();
1146  if ( maxStr != "" )
1147  maxVal = maxStr.toDouble();
1148 
1149  QPen linePen = QPen( mHistoColors.at( theBandNo ) );
1150  linePen.setStyle( Qt::DashLine );
1151  mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1152  mHistoMarkerMin->setLinePen( linePen );
1153  mHistoMarkerMin->setXValue( minVal );
1154  mHistoMarkerMin->show();
1155  mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1156  mHistoMarkerMax->setLinePen( linePen );
1157  mHistoMarkerMax->setXValue( maxVal );
1158  mHistoMarkerMax->show();
1159 
1160  mpPlot->replot();
1161 }
1162 
1163 
1164 QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1165 {
1166  QList< int > mySelectedBands;
1167 
1168  if ( mHistoShowBands != ShowAll )
1169  {
1170  if ( mHistoShowBands == ShowSelected )
1171  {
1172  mySelectedBands << cboHistoBand->currentIndex() + 1;
1173  }
1174  else if ( mHistoShowBands == ShowRGB )
1175  {
1176  mySelectedBands = rendererSelectedBands();
1177  }
1178  }
1179 
1180  return mySelectedBands;
1181 }
1182 
1183 QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1184 {
1185  QList< int > mySelectedBands;
1186 
1187  if ( ! mRendererWidget )
1188  {
1189  mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1190  return mySelectedBands;
1191  }
1192 
1193  if ( mRendererName == "singlebandgray" )
1194  {
1195  mySelectedBands << mRendererWidget->selectedBand();
1196  }
1197  else if ( mRendererName == "multibandcolor" )
1198  {
1199  for ( int i = 0; i <= 2; i++ )
1200  {
1201  mySelectedBands << mRendererWidget->selectedBand( i );
1202  }
1203  }
1204 
1205  return mySelectedBands;
1206 }
1207 
1208 QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int theBandNo )
1209 {
1210  QPair< QString, QString > myMinMax;
1211 
1212  if ( ! mRendererWidget )
1213  return myMinMax;
1214 
1215  if ( mRendererName == "singlebandgray" )
1216  {
1217  if ( theBandNo == mRendererWidget->selectedBand() )
1218  {
1219  myMinMax.first = mRendererWidget->min();
1220  myMinMax.second = mRendererWidget->max();
1221  }
1222  }
1223  else if ( mRendererName == "multibandcolor" )
1224  {
1225  for ( int i = 0; i <= 2; i++ )
1226  {
1227  if ( theBandNo == mRendererWidget->selectedBand( i ) )
1228  {
1229  myMinMax.first = mRendererWidget->min( i );
1230  myMinMax.second = mRendererWidget->max( i );
1231  break;
1232  }
1233  }
1234  }
1235 
1236  // TODO - there are 2 definitions of raster data type that should be unified
1237  // QgsRasterDataProvider::DataType and QGis::DataType
1238  // TODO - fix gdal provider: changes data type when nodata value is not found
1239  // this prevents us from getting proper min and max values here
1240  // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( QGis::DataType )
1241  // mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
1242  // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( QGis::DataType )
1243  // mRasterLayer->dataProvider()->dataType( theBandNo ) ) );
1244 
1245  // if we get an empty result, fill with default value (histo min/max)
1246  if ( myMinMax.first.isEmpty() )
1247  myMinMax.first = QString::number( mHistoMin );
1248  if ( myMinMax.second.isEmpty() )
1249  myMinMax.second = QString::number( mHistoMax );
1250 
1251  QgsDebugMsg( QString( "bandNo %1 got min/max [%2] [%3]" ).arg( theBandNo ).arg( myMinMax.first ).arg( myMinMax.second ) );
1252 
1253  return myMinMax;
1254 }
void setActive(bool theActiveFlag)
Activate the histogram widget.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
double minimum
The minimum histogram value.
QgsRasterHistogramWidget(QgsRasterLayer *lyr, QWidget *parent=0)
static double maximumValuePossible(QGis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
static QPixmap getThemePixmap(const QString &theName)
Helper to get a theme icon as a pixmap.
static double minimumValuePossible(QGis::DataType)
Helper function that returns the minimum possible value for a GDAL data type.
virtual bool hasHistogram(int theBandNo, int theBinCount, double theMinimum=std::numeric_limits< double >::quiet_NaN(), double theMaximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0, bool theIncludeOutOfRange=false)
Returns true if histogram is available (cached, already calculated), the parameters are the same as i...
virtual 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...
virtual int selectedBand(int index=0)
const QString bandName(int theBandNoInt)
Get the name of a band given its number.
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
DataType
Raster data types.
Definition: qgis.h:204
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.
int min(int a, int b)
Definition: util.h:93
double maximum
The maximum histogram value.
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.
void setRendererWidget(const QString &name, QgsRasterRendererWidget *rendererWidget=NULL)
Set the renderer widget (or just its name if there is no widget)
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
The QgsRasterHistogram is a container for histogram of a single raster band.
virtual void setMin(QString value, int index=0)
int binCount
Number of bins (intervals,buckets) in histogram.
virtual QString max(int index=0)
QgsRasterDataProvider * dataProvider()
Returns the data provider.
QString findClosestTickVal(double target, const QwtScaleDiv *scale, int div=100)
bool histoSaveAsImage(const QString &theFilename, int width=600, int height=600, int quality=-1)
Save the histogram as an image to disk.
HistogramVector histogramVector
Store the histogram for a given layer.
int max(int a, int b)
Definition: util.h:87
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
virtual void setMax(QString value, int index=0)
QPair< QString, QString > GUI_EXPORT getSaveAsImageName(QWidget *theParent, QString theMessage, QString defaultFilename)
A helper function to get an image name from the user.
Definition: qgisgui.cpp:86
#define tr(sourceText)