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