QGIS API Documentation  2.10.1-Pisa
 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 #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 
251 {
252  mRendererName = name;
253  mRendererWidget = rendererWidget;
255  on_cboHistoBand_currentIndexChanged( -1 );
256 }
257 
258 void QgsRasterHistogramWidget::setActive( bool theActiveFlag )
259 {
260  if ( theActiveFlag )
261  {
263  on_cboHistoBand_currentIndexChanged( -1 );
264  }
265  else
266  {
269  btnHistoMin->setChecked( false );
270  btnHistoMax->setChecked( false );
271  }
272 }
273 
274 void QgsRasterHistogramWidget::on_btnHistoCompute_clicked()
275 {
276 // Histogram computation can be called either by clicking the "Compute Histogram" button
277 // which is only visible if there is no cached histogram or by calling the
278 // "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
279 // to re-calculate the histogram if it has already been calculated
280  computeHistogram( true );
282 }
283 
284 bool QgsRasterHistogramWidget::computeHistogram( bool forceComputeFlag )
285 {
286  QgsDebugMsg( "entered." );
287 
288  //bool myIgnoreOutOfRangeFlag = true;
289  //bool myThoroughBandScanFlag = false;
290  int myBandCountInt = mRasterLayer->bandCount();
291 
292  // if forceComputeFlag = false make sure raster has cached histogram, else return false
293  if ( ! forceComputeFlag )
294  {
295  for ( int myIteratorInt = 1;
296  myIteratorInt <= myBandCountInt;
297  ++myIteratorInt )
298  {
299  int sampleSize = 250000; // number of sample cells
300  if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
301  {
302  QgsDebugMsg( QString( "band %1 does not have cached histo" ).arg( myIteratorInt ) );
303  return false;
304  }
305  }
306  }
307 
308  // compute histogram
309  stackedWidget2->setCurrentIndex( 1 );
310  connect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
311  QApplication::setOverrideCursor( Qt::WaitCursor );
312 
313  for ( int myIteratorInt = 1;
314  myIteratorInt <= myBandCountInt;
315  ++myIteratorInt )
316  {
317  int sampleSize = 250000; // number of sample cells
318  mRasterLayer->dataProvider()->histogram( myIteratorInt, 0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize );
319  }
320 
321  disconnect( mRasterLayer, SIGNAL( progressUpdate( int ) ), mHistogramProgress, SLOT( setValue( int ) ) );
322  // mHistogramProgress->hide();
323  stackedWidget2->setCurrentIndex( 0 );
325 
326  return true;
327 }
328 
329 
331 {
332  // Explanation:
333  // We use the gdal histogram creation routine is called for each selected
334  // layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
335  // the total number of cells that fit into the range defined by that bin.
336  //
337  // The graph routine below determines the greatest number of pixels in any given
338  // bin in all selected layers, and the min. It then draws a scaled line between min
339  // and max - scaled to image height. 1 line drawn per selected band
340  //
341  int myBandCountInt = mRasterLayer->bandCount();
342 
343  QgsDebugMsg( "entered." );
344 
345  if ( ! computeHistogram( false ) )
346  {
347  QgsDebugMsg( QString( "raster does not have cached histogram" ) );
348  stackedWidget2->setCurrentIndex( 2 );
349  return;
350  }
351 
352  // clear plot
353  mpPlot->detachItems();
354 
355  //ensure all children get removed
356  mpPlot->setAutoDelete( true );
357  mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
358  mpPlot->insertLegend( new QwtLegend(), QwtPlot::BottomLegend );
359  // Set axis titles
360  mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Pixel Value" ) );
361  mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Frequency" ) );
362  mpPlot->setAxisAutoScale( QwtPlot::yLeft );
363 
364  // x axis scale only set after computing global min/max across bands (see below)
365  // add a grid
366  QwtPlotGrid * myGrid = new QwtPlotGrid();
367  myGrid->attach( mpPlot );
368 
369  // make colors list
370  mHistoColors.clear();
371  mHistoColors << Qt::black; // first element, not used
372  QVector<QColor> myColors;
373  myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
374  qsrand( myBandCountInt * 100 ); // make sure colors are always the same for a given band count
375  while ( myColors.size() <= myBandCountInt )
376  {
377  myColors <<
378  QColor( 1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
379  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ),
380  1 + ( int )( 255.0 * qrand() / ( RAND_MAX + 1.0 ) ) );
381  }
382  //randomise seed again
383  qsrand( time( 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 );
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 
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  {
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  {
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  }
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 setText(const QString &text)
void setActive(bool theActiveFlag)
Activate the histogram widget.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void setStyle(Qt::PenStyle style)
void setupUi(QWidget *widget)
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
bool end()
void setSeparator(bool b)
void append(const T &value)
void fill(const QColor &color)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
void setRight(qreal x)
void setChecked(bool)
QVariant data() const
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
double minimum
The minimum histogram value.
QgsRasterHistogramWidget(QgsRasterLayer *lyr, QWidget *parent=0)
void addAction(QAction *action)
static double maximumValuePossible(QGis::DataType)
Helper function that returns the maximum possible value for a GDAL data type.
T & first()
double toDouble(bool *ok) const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString tr(const char *sourceText, const char *disambiguation, int n)
static QPixmap getThemePixmap(const QString &theName)
Helper to get a theme icon as a pixmap.
void setLeft(qreal x)
int size() const
void setValue(const QString &key, const QVariant &value)
const char * name() const
void clear()
QString number(int n, int base)
static double minimumValuePossible(QGis::DataType)
Helper function that returns the minimum possible value for a GDAL data type.
qreal x() const
virtual bool hasHistogram(int theBandNo, int theBinCount, double theMinimum=std::numeric_limits< double >::quiet_NaN(), double theMaximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0, bool theIncludeOutOfRange=false)
Returns true if histogram is available (cached, already calculated), the parameters are the same as i...
bool save(const QString &fileName, const char *format, int quality) const
virtual QString min(int index=0)
int bandCount() const
Get the number of bands in this layer.
virtual QGis::DataType srcDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
bool isEmpty() const
bool isEmpty() const
void remove(int i)
virtual int selectedBand(int index=0)
void setOverrideCursor(const QCursor &cursor)
const QString bandName(int theBandNoInt)
Get the name of a band given its number.
void restoreOverrideCursor()
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
QDir dir() const
QRect rect() const
virtual QgsRasterHistogram histogram(int theBandNo, int theBinCount=0, double theMinimum=std::numeric_limits< double >::quiet_NaN(), double theMaximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0, bool theIncludeOutOfRange=false)
Get histogram.
void setData(const QVariant &userData)
int min(int a, int b)
Definition: util.h:93
double maximum
The maximum histogram value.
bool contains(const T &value) const
void setCheckable(bool)
void on_mSaveAsImageButton_clicked()
This slot lets you save the histogram as an image to disk.
virtual QGis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
const T & at(int i) const
QVariant value(const QString &key, const QVariant &defaultValue) const
void setRendererWidget(const QString &name, QgsRasterRendererWidget *rendererWidget=NULL)
Set the renderer widget (or just its name if there is no widget)
bool isEmpty() const
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
DataType
Raster data types.
Definition: qgis.h:204
bool toBool() const
void pop_front()
QString left(int n) const
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.
void setSeparatorsCollapsible(bool collapse)
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.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int size() const
bool begin(QPaintDevice *device)
void setExclusive(bool)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
int max(int a, int b)
Definition: util.h:87
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
QString baseName() const
void setEnabled(bool)
QCursor * overrideCursor()
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