QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgshistogramwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgshistogramwidget.cpp
3  ----------------------
4  begin : May 2015
5  copyright : (C) 2015 by Nyall Dawson
6  email : nyall dot dawson 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 "qgshistogramwidget.h"
19 #include "qgsapplication.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsvectorlayerutils.h"
22 #include "qgsstatisticalsummary.h"
23 #include "qgssettings.h"
24 
25 #include <QObject>
26 #include <QMouseEvent>
27 
28 // QWT Charting widget
29 #include <qwt_global.h>
30 #include <qwt_plot_canvas.h>
31 #include <qwt_plot.h>
32 #include <qwt_plot_curve.h>
33 #include <qwt_plot_grid.h>
34 #include <qwt_plot_marker.h>
35 #include <qwt_plot_picker.h>
36 #include <qwt_picker_machine.h>
37 #include <qwt_plot_layout.h>
38 #include <qwt_plot_renderer.h>
39 #include <qwt_plot_histogram.h>
40 #include <qwt_text.h>
41 
42 
43 QgsHistogramWidget::QgsHistogramWidget( QWidget *parent, QgsVectorLayer *layer, const QString &fieldOrExp )
44  : QWidget( parent )
45  , mVectorLayer( layer )
46  , mSourceFieldExp( fieldOrExp )
47  , mXAxisTitle( QObject::tr( "Value" ) )
48  , mYAxisTitle( QObject::tr( "Count" ) )
49 {
50  setupUi( this );
51 
52  mPlot = mpPlot;
53 
54  // hide the ugly canvas frame
55  QFrame *plotCanvasFrame = dynamic_cast<QFrame *>( mpPlot->canvas() );
56  if ( plotCanvasFrame )
57  plotCanvasFrame->setFrameStyle( QFrame::NoFrame );
58 
59  QgsSettings settings;
60  mMeanCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showMean" ), false ).toBool() );
61  mStdevCheckBox->setChecked( settings.value( QStringLiteral( "HistogramWidget/showStdev" ), false ).toBool() );
62 
63  connect( mBinsSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsHistogramWidget::refresh );
64  connect( mMeanCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
65  connect( mStdevCheckBox, &QAbstractButton::toggled, this, &QgsHistogramWidget::refresh );
66  connect( mLoadValuesButton, &QAbstractButton::clicked, this, &QgsHistogramWidget::refreshValues );
67 
68  mGridPen = QPen( QColor( 0, 0, 0, 40 ) );
69  mMeanPen = QPen( QColor( 10, 10, 10, 220 ) );
70  mMeanPen.setStyle( Qt::DashLine );
71  mStdevPen = QPen( QColor( 30, 30, 30, 200 ) );
72  mStdevPen.setStyle( Qt::DashLine );
73 
74  if ( layer && !mSourceFieldExp.isEmpty() )
75  {
76  refresh();
77  }
78 }
79 
81 {
82  QgsSettings settings;
83  settings.setValue( QStringLiteral( "HistogramWidget/showMean" ), mMeanCheckBox->isChecked() );
84  settings.setValue( QStringLiteral( "HistogramWidget/showStdev" ), mStdevCheckBox->isChecked() );
85 }
86 
87 static bool _rangesByLower( const QgsRendererRange &a, const QgsRendererRange &b )
88 {
89  return a.lowerValue() < b.lowerValue();
90 }
91 
93 {
94  mRanges = ranges;
95  std::sort( mRanges.begin(), mRanges.end(), _rangesByLower );
96 }
97 
99 {
100  mValues.clear();
101 
102  if ( !mVectorLayer || mSourceFieldExp.isEmpty() )
103  return;
104 
105  QApplication::setOverrideCursor( Qt::WaitCursor );
106 
107  bool ok;
108  mValues = QgsVectorLayerUtils::getDoubleValues( mVectorLayer, mSourceFieldExp, ok );
109 
110  if ( ! ok )
111  {
112  QApplication::restoreOverrideCursor();
113  return;
114  }
115 
116 
117  std::sort( mValues.begin(), mValues.end() );
118  mHistogram.setValues( mValues );
119  mBinsSpinBox->blockSignals( true );
120  mBinsSpinBox->setValue( std::max( mHistogram.optimalNumberBins(), 30 ) );
121  mBinsSpinBox->blockSignals( false );
122 
124  mStats.calculate( mValues );
125 
126  mpPlot->setEnabled( true );
127  mMeanCheckBox->setEnabled( true );
128  mStdevCheckBox->setEnabled( true );
129  mBinsSpinBox->setEnabled( true );
130 
131  QApplication::restoreOverrideCursor();
132 
133  //also force a redraw
134  refresh();
135 }
136 
138 {
139  drawHistogram();
140 }
141 
143 {
144  if ( layer == mVectorLayer )
145  return;
146 
147  mVectorLayer = layer;
148  clearHistogram();
149 }
150 
151 void QgsHistogramWidget::clearHistogram()
152 {
153  mValues.clear();
154  mHistogram.setValues( mValues );
155  refresh();
156 
157  mpPlot->setEnabled( false );
158  mMeanCheckBox->setEnabled( false );
159  mStdevCheckBox->setEnabled( false );
160  mBinsSpinBox->setEnabled( false );
161 }
162 
163 void QgsHistogramWidget::setSourceFieldExp( const QString &fieldOrExp )
164 {
165  if ( fieldOrExp == mSourceFieldExp )
166  return;
167 
168  mSourceFieldExp = fieldOrExp;
169  clearHistogram();
170 }
171 
173 {
174  // clear plot
175  mpPlot->detachItems();
176 
177  //ensure all children get removed
178  mpPlot->setAutoDelete( true );
179  // Set axis titles
180  if ( !mXAxisTitle.isEmpty() )
181  mpPlot->setAxisTitle( QwtPlot::xBottom, mXAxisTitle );
182  if ( !mYAxisTitle.isEmpty() )
183  mpPlot->setAxisTitle( QwtPlot::yLeft, mYAxisTitle );
184  mpPlot->setAxisFont( QwtPlot::xBottom, this->font() );
185  mpPlot->setAxisFont( QwtPlot::yLeft, this->font() );
186  QFont titleFont = this->font();
187  titleFont.setBold( true );
188  QwtText xAxisText = mpPlot->axisTitle( QwtPlot::xBottom );
189  xAxisText.setFont( titleFont );
190  mpPlot->setAxisTitle( QwtPlot::xBottom, xAxisText );
191  QwtText yAxisText = mpPlot->axisTitle( QwtPlot::yLeft );
192  yAxisText.setFont( titleFont );
193  mpPlot->setAxisTitle( QwtPlot::yLeft, yAxisText );
194  mpPlot->setAxisAutoScale( QwtPlot::yLeft );
195  mpPlot->setAxisAutoScale( QwtPlot::xBottom );
196 
197  // add a grid
198  QwtPlotGrid *grid = new QwtPlotGrid();
199  grid->enableX( false );
200  grid->setPen( mGridPen );
201  grid->attach( mpPlot );
202 
203  // make colors list
204  mHistoColors.clear();
205  for ( const QgsRendererRange &range : std::as_const( mRanges ) )
206  {
207  mHistoColors << ( range.symbol() ? range.symbol()->color() : Qt::black );
208  }
209 
210  //draw histogram
211  QwtPlotHistogram *plotHistogram = nullptr;
212  plotHistogram = createPlotHistogram( !mRanges.isEmpty() ? mRanges.at( 0 ).label() : QString(),
213  !mRanges.isEmpty() ? QBrush( mHistoColors.at( 0 ) ) : mBrush,
214  !mRanges.isEmpty() ? Qt::NoPen : mPen );
215  QVector<QwtIntervalSample> dataHisto;
216 
217  int bins = mBinsSpinBox->value();
218  QList<double> edges = mHistogram.binEdges( bins );
219  QList<int> counts = mHistogram.counts( bins );
220 
221  int rangeIndex = 0;
222  int lastValue = 0;
223 
224  for ( int bin = 0; bin < bins; ++bin )
225  {
226  int binValue = counts.at( bin );
227 
228  //current bin crosses two graduated ranges, so we split between
229  //two histogram items
230  if ( rangeIndex < mRanges.count() - 1 && edges.at( bin ) > mRanges.at( rangeIndex ).upperValue() )
231  {
232  rangeIndex++;
233  plotHistogram->setSamples( dataHisto );
234  plotHistogram->attach( mpPlot );
235  plotHistogram = createPlotHistogram( mRanges.at( rangeIndex ).label(), mHistoColors.at( rangeIndex ) );
236  dataHisto.clear();
237  dataHisto << QwtIntervalSample( lastValue, mRanges.at( rangeIndex - 1 ).upperValue(), edges.at( bin ) );
238  }
239 
240  double upperEdge = !mRanges.isEmpty() ? std::min( edges.at( bin + 1 ), mRanges.at( rangeIndex ).upperValue() )
241  : edges.at( bin + 1 );
242 
243  dataHisto << QwtIntervalSample( binValue, edges.at( bin ), upperEdge );
244 
245  lastValue = binValue;
246  }
247 
248  plotHistogram->setSamples( dataHisto );
249  plotHistogram->attach( mpPlot );
250 
251  mRangeMarkers.clear();
252  for ( const QgsRendererRange &range : std::as_const( mRanges ) )
253  {
254  QwtPlotMarker *rangeMarker = new QwtPlotMarker();
255  rangeMarker->attach( mpPlot );
256  rangeMarker->setLineStyle( QwtPlotMarker::VLine );
257  rangeMarker->setXValue( range.upperValue() );
258  rangeMarker->setLabel( QLocale().toString( range.upperValue() ) );
259  rangeMarker->setLabelOrientation( Qt::Vertical );
260  rangeMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
261  rangeMarker->show();
262  mRangeMarkers << rangeMarker;
263  }
264 
265  if ( mMeanCheckBox->isChecked() )
266  {
267  QwtPlotMarker *meanMarker = new QwtPlotMarker();
268  meanMarker->attach( mpPlot );
269  meanMarker->setLineStyle( QwtPlotMarker::VLine );
270  meanMarker->setLinePen( mMeanPen );
271  meanMarker->setXValue( mStats.mean() );
272  meanMarker->setLabel( QString( QChar( 956 ) ) );
273  meanMarker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
274  meanMarker->show();
275  }
276 
277  if ( mStdevCheckBox->isChecked() )
278  {
279  QwtPlotMarker *stdev1Marker = new QwtPlotMarker();
280  stdev1Marker->attach( mpPlot );
281  stdev1Marker->setLineStyle( QwtPlotMarker::VLine );
282  stdev1Marker->setLinePen( mStdevPen );
283  stdev1Marker->setXValue( mStats.mean() - mStats.stDev() );
284  stdev1Marker->setLabel( QString( QChar( 963 ) ) );
285  stdev1Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
286  stdev1Marker->show();
287 
288  QwtPlotMarker *stdev2Marker = new QwtPlotMarker();
289  stdev2Marker->attach( mpPlot );
290  stdev2Marker->setLineStyle( QwtPlotMarker::VLine );
291  stdev2Marker->setLinePen( mStdevPen );
292  stdev2Marker->setXValue( mStats.mean() + mStats.stDev() );
293  stdev2Marker->setLabel( QString( QChar( 963 ) ) );
294  stdev2Marker->setLabelAlignment( Qt::AlignLeft | Qt::AlignTop );
295  stdev2Marker->show();
296  }
297 
298  mpPlot->setEnabled( true );
299  mpPlot->replot();
300 }
301 
302 QwtPlotHistogram *QgsHistogramWidget::createPlotHistogram( const QString &title, const QBrush &brush, const QPen &pen ) const
303 {
304  QwtPlotHistogram *histogram = new QwtPlotHistogram( title );
305  histogram->setBrush( brush );
306  if ( pen != Qt::NoPen )
307  {
308  histogram->setPen( pen );
309  }
310  else if ( brush.color().lightness() > 200 )
311  {
312  QPen p;
313  p.setColor( brush.color().darker( 150 ) );
314  p.setWidth( 0 );
315  p.setCosmetic( true );
316  histogram->setPen( p );
317  }
318  else
319  {
320  histogram->setPen( QPen( Qt::NoPen ) );
321  }
322  return histogram;
323 }
324 
QgsStatisticalSummary::setStatistics
void setStatistics(QgsStatisticalSummary::Statistics stats)
Sets flags which specify which statistics will be calculated.
Definition: qgsstatisticalsummary.cpp:33
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsStatisticalSummary::StDev
@ StDev
Standard deviation of values.
Definition: qgsstatisticalsummary.h:55
QgsHistogram::setValues
void setValues(const QList< double > &values)
Assigns numeric source values for the histogram.
Definition: qgshistogram.cpp:36
QgsHistogramWidget::refresh
void refresh()
Redraws the histogram.
Definition: qgshistogramwidget.cpp:137
QgsRendererRange
Definition: qgsrendererrange.h:36
QgsHistogramWidget::setLayer
void setLayer(QgsVectorLayer *layer)
Sets the vector layer associated with the histogram.
Definition: qgshistogramwidget.cpp:142
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsRendererRange::lowerValue
double lowerValue() const
Returns the lower bound of the range.
Definition: qgsrendererrange.cpp:74
qgsstatisticalsummary.h
QgsHistogramWidget::mRanges
QgsRangeList mRanges
Definition: qgshistogramwidget.h:195
QgsHistogramWidget::pen
QPen pen() const
Returns the pen used when drawing histogram bars.
Definition: qgshistogramwidget.h:96
qgsapplication.h
QgsHistogramWidget::setGraduatedRanges
void setGraduatedRanges(const QgsRangeList &ranges)
Sets the graduated ranges associated with the histogram.
Definition: qgshistogramwidget.cpp:92
QgsHistogramWidget::mPlot
QwtPlot * mPlot
Definition: qgshistogramwidget.h:194
QgsRangeList
QList< QgsRendererRange > QgsRangeList
Definition: qgsrendererrange.h:191
QgsStatisticalSummary::stDev
double stDev() const
Returns population standard deviation.
Definition: qgsstatisticalsummary.h:226
QgsHistogramWidget::mRangeMarkers
QList< QwtPlotMarker * > mRangeMarkers
Definition: qgshistogramwidget.h:196
QgsHistogramWidget::refreshValues
void refreshValues()
Refreshes the values for the histogram by fetching them from the layer.
Definition: qgshistogramwidget.cpp:98
qgsvectorlayerutils.h
QgsHistogram::optimalNumberBins
int optimalNumberBins() const
Returns the optimal number of bins for the source values, calculated using the Freedman-Diaconis rule...
Definition: qgshistogram.cpp:63
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
qgsvectorlayer.h
QgsStatisticalSummary::mean
double mean() const
Returns calculated mean of values.
Definition: qgsstatisticalsummary.h:177
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
qgssettings.h
QgsHistogramWidget::layer
QgsVectorLayer * layer()
Returns the layer currently associated with the widget.
Definition: qgshistogramwidget.h:71
QgsHistogram::binEdges
QList< double > binEdges(int bins) const
Returns a list of edges for the histogram for a specified number of bins.
Definition: qgshistogram.cpp:68
qgshistogramwidget.h
QgsHistogramWidget::QgsHistogramWidget
QgsHistogramWidget(QWidget *parent=nullptr, QgsVectorLayer *layer=nullptr, const QString &fieldOrExp=QString())
QgsHistogramWidget constructor.
Definition: qgshistogramwidget.cpp:43
QgsHistogramWidget::~QgsHistogramWidget
~QgsHistogramWidget() override
Definition: qgshistogramwidget.cpp:80
QgsHistogramWidget::drawHistogram
virtual void drawHistogram()
Updates and redraws the histogram.
Definition: qgshistogramwidget.cpp:172
QgsHistogramWidget::brush
QBrush brush() const
Returns the brush used when drawing histogram bars.
Definition: qgshistogramwidget.h:112
QgsHistogramWidget::setSourceFieldExp
void setSourceFieldExp(const QString &fieldOrExp)
Sets the source field or expression to use for values in the histogram.
Definition: qgshistogramwidget.cpp:163
QgsHistogram::counts
QList< int > counts(int bins) const
Returns the calculated list of the counts for the histogram bins.
Definition: qgshistogram.cpp:84
QgsStatisticalSummary::calculate
void calculate(const QList< double > &values)
Calculates summary statistics for a list of values.
Definition: qgsstatisticalsummary.cpp:72
QgsVectorLayerUtils::getDoubleValues
static QList< double > getDoubleValues(const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr, QgsFeedback *feedback=nullptr)
Fetches all double values from a specified field name or expression.
Definition: qgsvectorlayerutils.cpp:129