subsurface/stats/barseries.h
Berthold Stoeger ca572acb0d statistics: implement a bar series
Implement a bar series, which can plot stacked, grouped and single
bar charts in horizontal or vertical ways. On hovering over a
bar, an information is shown. The shown information depends on
whether the chart is count or value based, or is a multi-bin
chart.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00

140 lines
5 KiB
C++

// SPDX-License-Identifier: GPL-2.0
// A small custom bar series, which displays information
// when hovering over a bar. The QtCharts bar series were
// too inflexible with respect to placement of the bars.
#ifndef BAR_SERIES_H
#define BAR_SERIES_H
#include "statsseries.h"
#include "statsvariables.h"
#include <memory>
#include <vector>
#include <QGraphicsRectItem>
namespace QtCharts {
class QAbstractAxis;
}
class InformationBox;
class StatsVariable;
class BarSeries : public StatsSeries {
public:
// There are three versions of creating bar series: for value-based (mean, etc) charts, for counts
// based charts and for stacked bar charts with multiple items.
struct CountItem {
double lowerBound, upperBound;
int count;
std::vector<QString> label;
QString binName;
int total;
};
struct ValueItem {
double lowerBound, upperBound;
double value;
std::vector<QString> label;
QString binName;
StatsOperationResults res;
};
struct MultiItem {
double lowerBound, upperBound;
std::vector<std::pair<int, std::vector<QString>>> countLabels;
QString binName;
};
// If the horizontal flag is true, independent variable is plotted on the y-axis.
// A non-empty valueBinNames vector flags that this is a stacked bar chart.
// In that case, a valueVariable must also be provided.
// For count-based bar series in one variable, valueVariable is null.
// Note: this expects that all items are added with increasing pos
// and that no bar is inside another bar, i.e. lowerBound and upperBound
// are ordered identically.
BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, const QString &categoryName,
const std::vector<CountItem> &items);
BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, const QString &categoryName, const StatsVariable *valueVariable,
const std::vector<ValueItem> &items);
BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable,
std::vector<QString> valueBinNames,
const std::vector<MultiItem> &items);
~BarSeries();
void updatePositions() override;
bool hover(QPointF pos) override;
void unhighlight() override;
private:
BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable,
std::vector<QString> valueBinNames);
struct Index {
int bar;
int subitem;
Index();
Index(int bar, int subitem);
bool operator==(const Index &i2) const;
};
// Get item under mouse pointer, or -1 if none
Index getItemUnderMouse(const QPointF &f) const;
// A label that is composed of multiple lines
struct BarLabel {
std::vector<std::unique_ptr<QGraphicsSimpleTextItem>> items;
double totalWidth, totalHeight; // Size of the item
bool isOutside; // Is shown outside of bar
BarLabel(QtCharts::QChart *chart, const std::vector<QString> &labels, int bin_nr, int binCount);
void setVisible(bool visible);
void updatePosition(QtCharts::QChart *chart, QtCharts::QAbstractSeries *series,
bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount);
void highlight(bool highlight, int bin_nr, int binCount);
};
struct SubItem {
std::unique_ptr<QGraphicsRectItem> item;
std::unique_ptr<BarLabel> label;
double value_from;
double value_to;
int bin_nr;
void updatePosition(QtCharts::QChart *chart, BarSeries *series, bool horizontal, bool stacked,
double from, double to, int binCount);
void highlight(bool highlight, int binCount);
};
struct Item {
double lowerBound, upperBound;
std::vector<SubItem> subitems;
QRectF rect;
const QString binName;
StatsOperationResults res;
int total;
Item(QtCharts::QChart *chart, BarSeries *series, double lowerBound, double upperBound,
std::vector<SubItem> subitems,
const QString &binName, const StatsOperationResults &res, int total, bool horizontal,
bool stacked, int binCount);
void updatePosition(QtCharts::QChart *chart, BarSeries *series, bool horizontal, bool stacked, int binCount);
void highlight(int subitem, bool highlight, int binCount);
int getSubItemUnderMouse(const QPointF &f, bool horizontal, bool stacked) const;
};
std::unique_ptr<InformationBox> information;
std::vector<Item> items;
std::vector<BarLabel> barLabels;
bool horizontal;
bool stacked;
QString categoryName;
const StatsVariable *valueVariable; // null: this is count based
std::vector<QString> valueBinNames;
Index highlighted;
std::vector<SubItem> makeSubItems(double value, const std::vector<QString> &label) const;
std::vector<SubItem> makeSubItems(const std::vector<std::pair<double, std::vector<QString>>> &values) const;
void add_item(double lowerBound, double upperBound, std::vector<SubItem> subitems,
const QString &binName, const StatsOperationResults &res, int total, bool horizontal,
bool stacked);
std::vector<QString> makeInfo(const Item &item, int subitem) const;
int binCount() const;
};
#endif