Commit graph

153 commits

Author SHA1 Message Date
Robert C. Helling
d6712bc5ac Plot proper confidence regions
I was coninced that that rather than doing an order of
magnitude estimate of the confidence region it's better
to have the correct concave shapes that indicate the
95% confidence level for the regression line.

It also turned out that the previous expression was
missing a factor of 1/sqrt(n).

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Robert C. Helling
5775bd7b27 Silence some compiler warnings
This is what clang suggested in compiler warnings.

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Robert C. Helling
d83c9b5246 Indicate goodness of fit of regression line
The goodness of fit of a regression line is the percentage
of the variance of the y values that is explained by the
dependence on the x values.

Set the alpha value of the regression line to this goodness
of fit.

Further, set the width of the regression line to a standard
deviation of the values from the regression line valies.

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Dirk Hohndel
e610fb2481 statistics: add count property to chart list
In order to be able to correctly size the chart type popup, we'll need
access to the total count or rows as a property that signals changes to
QML.

The hack to use rowCount() as the READ function requires that rowCount()
can be called without argument, therefore the addition of a default
parent.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-13 11:39:36 -08:00
Berthold Stoeger
46bb242c69 mobile/statistics: export ChartListModel to QML
For QML, the roles have to be associated dynamically with
name. Moreover, the model has to be registered as a QML
type to make it accessible from QML.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-13 11:39:36 -08:00
Berthold Stoeger
108f6fed2f statistics: print ellipsis in case of too little space
In categorical axes all labels were printed leading to a big
tohu wa-bohu for two many bins. Therefore, if a label is
larger than the space between two ticks, replace by an ellipsis.

Adjust the size of the ellipsis (".", ".." or "...") to the
available space.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-11 13:29:15 +01:00
Berthold Stoeger
bdecd98ef5 statistics: consider overhang of horizontal axes
The old code didn't consider that labels can peak out of
horizontal axes if labels are under ticks.

This commit takes this into account. However, it must be
noted that this is only heuristics: Before setting the
size of the axes, the actual minimum and maximum label are
not known, because we round to "nice" numbers. But the
size of the axis can only be set after knowing the overhang,
leading to a circular dependency. Therefore, the code
currently simply uses the minimum and maximum value of
the data, hoping that the "nice" values will not format
to something significantly larger. We could do a multi-pass
scheme, but let's not for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-11 12:59:17 +01:00
Berthold Stoeger
233e686b92 statistics: split people binner
The people binner (called "buddies") is too coarse. Split into
buddies, dive guide and people (the old "buddies", which is
a combination of buddies and dive guide).

Reported-by: Peter Zaal <peter.zaal@gmail.com>
Reported-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 15:56:40 -08:00
Dirk Hohndel
b40354c7f2 build-system: compile stats code on mobile OSs
Android and iOS use qmake, so add the code to the .pro file.
This also removes all remnants of QCharts includes and uses and all the
references to QCharts in our various build systems.

That was a brief but extremely useful detour.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-10 15:16:52 -08:00
Berthold Stoeger
e7907c494f statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.

Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.

Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.

The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.

Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 15:16:52 -08:00
Berthold Stoeger
f6b857b8fe statistics: fix 20 m binner
Copy&paste error: the 20 m binner binned to 10 m.

Reported-by: Peter Zaal <peter.zaal@gmail.com>
Reported-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 14:45:12 -08:00
Berthold Stoeger
bbdd34d069 statistics: add count to box and whisker plots
When calculating the quartiles, we need the count of dives
anyway, which makes it trivial to export this value to
the frontend.

Fixes an erroneous "mean", which should be "median".

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 14:45:12 -08:00
Berthold Stoeger
de62638b73 statistics: add tags variable
Trivially a copy of the "buddies" code.

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 13:42:48 -08:00
Berthold Stoeger
2d864c3e9d statistics: sort dive sites
The dive sites where sorted by location in RAM, which is just
silly. Add a DiveSiteWrapper that sorts by name, though that
should probably be improved.

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 13:42:48 -08:00
Berthold Stoeger
0efcbb0eb6 statistics: add min and max operations for numerical types
This makes sense and is easy to implement.

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 13:42:48 -08:00
Berthold Stoeger
b5c8d0dbb4 statistics: fix range in categorical axes
The range for a one-bin chart is [-0.5,0.5], thus the range
in an n-bin chart is [-0.5,n-0.5], not [-0.5,n+0.5].

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-09 14:25:02 -08:00
Berthold Stoeger
83848fb2a8 statistics: add a dive-# variable
This was requested on the mailing list and it makes sense to
have it. Of course, not all charts make sense: e.g. a plot dive-#
vs. count is a bit redundant...

Sadly, this can't use the generic IntRangeBinner, because dive-#s
start at 1, not 0.

Suggested-by: Christof Arnosti <charno@charno.ch>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-09 14:17:56 -08:00
Berthold Stoeger
b00009ef4e statistics: add a mean depth variable
This was requested on the mailing list. Reduce code size somewhat
by deriving the binner and the variable classes from common
base classes with a mean-vs-max flag.

Suggested-by: Christof Arnosti <charno@charno.ch>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-09 14:17:56 -08:00
Berthold Stoeger
2a850025b2 statistics: add ticks at beginning and end of the axis
The grid is based on the axis ticks. If labels in histogram
axes were skipped (because there are too many bins), it could
happen that the grid was incomplete, because the first and/or
last tick were missing. Add these explicitly.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 10:39:52 -08:00
Berthold Stoeger
cad00032fc statistics: improve placement of info-box
The info box was placed either above or below the mouse-pointer.
If the pointer is at the center and the infobox higher than
half the chart, it would cross the border. Detect this case
and place the info box at the center.

Same logic for right/left, though that should typically not
happen.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
405e6b6b69 statistics: fix chart features: regression line and median/mean
The coordinates of these were calculated when creating the feature.
This is wrong, because the min/max values of the axes can change
on resize to get "nice" number. Therefore, recalculate after resizing.
This means that the general "LineMarker" class has to be split into
two classes, one for regression lines and one for median/mean
markers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
ab324ed769 statistics: paint custom grid
With removal of QtCharts' axes, the grid was lost. Readd it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
8dfa3f6db3 statistics: draw title of axes
Easy enough to implement, but one weirdness:
To get the height of the rotated text, one has to access the
width() member of the boundingRect. I'm not sure if that makes
sense, but so be it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
4ab9f1c6b0 statistics: replace QtCharts' axes
Replace by custom implementation, with the ultimate goal to
remove the QtCharts module. This doesn't yet display axis
titles or a grid.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
598058e21e statistics: set correct z-value for bars
The bars were set to the z-value of the labels. Not an issue,
since the labels are generated after the bars and therefore
plot later. Still, do the right thing.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
90129aa26f statistics: render title
Since we want to get rid of QtCharts, we have to render our own
title. Simply keep around a QGraphicsSimpleTextItem and put in
the center of the chart. Define the borders to the scene as
constants.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
ccc95f938a statistics: save axis with series
In the future we want to use our own axis implementation to
convert from/to screen coordinates. For this purpose, we
need to save the axes with the series. Especially if we want
to support multiple series on different axes.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Berthold Stoeger
23d781deba statistics: save chart in axis class
The chart was passed as argument to the function recalculating
the axis labels. Instead, pass the chart in the constructor of
the axes and save it. This gains us flexibility for the future:
There will be more functions that need to access the chart (e.g.
resizing of the axes).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-06 12:31:22 -08:00
Robert C. Helling
b181c011cc Add dive rating and visibility to statistics variables
Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-05 00:34:14 +01:00
Dirk Hohndel
ce0f64df2e stats: fix line segment intersection math
Linear algebra class was a while ago, but somehow this does look more
logical to me.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-04 11:20:51 -08:00
Dirk Hohndel
1f1d82b1b3 statistics: don't crash if the QCharts QML modules aren't found
We don't really give a user visible error message which is kind of a problem,
but at least we don't crash anymore.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-03 16:58:01 -08:00
Berthold Stoeger
6a6dd0888a statistics: don't reset binner if binner is set
Recently code was added to reset variable 1 binner if the second
variable does not support an unbinned first variable.

It forgot to check whether a binner was already set. Do this.
But validate the old binner first!

This code is extremely fragile and will have to be redone.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 15:41:36 -08:00
Berthold Stoeger
bea289e314 statistics: clip regression line
A steep regression line would shoot out of the chart. Therefore,
clip to the y = minY and y = maxY lines.

QtGraphicsScene has its own clipping routines, but they are
very general, so let's do this trivial case by hand.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:56:05 -08:00
Berthold Stoeger
9beec46e22 statistics: use RoundRectItem for legend and info-box
Dirk says rounded corners look better. This now looks a bit
extreme to me and probably the border size should be increased.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:56:05 -08:00
Berthold Stoeger
9759d5b21a statistics: add parentheses around percentage in horizontal bars
For better visual guidance, format labels as "count (percentage)"
in horizontal bar charts. In vertical bar charts two lines are used
anyway.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:56:05 -08:00
Dirk Hohndel
e47878cbce statistics: charts drop down looks better with smaller icons
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-03 13:56:01 -08:00
Berthold Stoeger
9c4157ca35 statistics: fix silly typo in legend code
After each column, instead of setting the new x-variable, the
new value was added to the old value. This led to ever increasing
gaps.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:41:15 -08:00
Berthold Stoeger
7b0455b4d8 statistics: reverse chart selection logic
The old ways was to select the chart first, then depending on
the chart choose the binning.

Willem says that it should work the other way round: select
the binning (or operation) and make the charts depend on
that.

I'm not arguing one way or the other, just note that the new
way is much more tricky, because it is easy to get unsupported
combinations. For example, there is no chart where the
first variable is unbinned, but the second axis is binned
or has an operation. This makes things distinctly more tricky
and this code still needs a thorough audit.

Since this is all more tricky, implement a "invalid" chart
state. Ideally that should be never shown to the user, but
let's try to be defensive.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:41:15 -08:00
Berthold Stoeger
319a7af31a statistics: add a model that describes a list of charts
Qt's comboboxes are controlled by models, there's no way around
that. To customize the chart-selection widget this must therefore
be abstracted into a model. On the upside, this hopefully can
be used for desktop and mobile.

The model provides icons and paints a warning-symbol on it
if the statistics core code deems the chart to be not recommended.
Notably, when plotting a categorical bar chart against a
numerical value (in such a case histograms are preferred).

Includes a fix for a silly oversight in CMakelist.txt: add the
statstranslations.h header.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:41:15 -08:00
Berthold Stoeger
76d94eda23 statistics: silence two Coverity warnings
Two warnings concerning division by zero and non-initialization
of a member variable, respectively.

Both are false positives. However, Coverity is excused because
it probably doesn't understand std::vector<> and also can't
know whether the object in question is generated in a different
source file.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-03 13:41:15 -08:00
Berthold Stoeger
995100a540 statistics: implement StatsView
The StatsView shows the chart described by the StatsState structure.
It is based on a QML ChartView. This should make it possible to
easily port to mobile. It does not include any of the UI around
the chart, viz. the variable and chart selection, etc.

The code checking for the statistical significance of the regression
line was written by Willem.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: willemferguson <willemferguson@zoology.up.ac.za>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
a034014a6a statistics: implement a structure representing the chart state
The StatsState structure fully describes the current state of
the chart: the selected axes, operations and additional chart
features, such as legend or labels.

The code implements sanity checks and reacts accordingly,
if an invalid combination of variables and charts is chosen.

The chart and variable lists to be displayed can be queried
and are encapsulated in the StatsState::UIState structure.

Some variable / chart combinations are possible, but not
recommended, which is represented by a warning flag.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
cccc0abc0c statistics: implement scatter series
Implement a simple scatter series for plotting two numerical variables
agains each other. Since the scatter symbols may overlap, on hover
multiple dives are shown in the information box. If the box
would become too large, only the first few dives are shown followed
by "and X more".

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
ad7872424b statistics: implement pie series
Implement a simple count-based pie chart. Percentage labels
are shown in the pie slices, the names outside the pie slices.
On hovering over a slice, the actual counts are shown.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
b0bdef469e statistics: implement a box-and-whisker series
Implements a simple box-and-whisker series to display
quartile based data. When hovering over a box-and-whiskers
item the precise data of the quartiles is shown.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
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
Berthold Stoeger
99f98ea6d4 statistics: implement a simple information box
When the user hovers over features in the chart, they should
be presented with more information. For example in bar charts
on the dives the bar represents and the exact value that the
bar represents, etc.

The InformationBox is a simple QGraphicsWidget, which can be
placed on top of QCharts and can show a number of arbitrary
text lines.

When placing the box on the chart, the code attempts to stay
inside the plot area of the chart.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
907e6ff05d statistics: add series interface class
Add a interface class for the chart series used by the statistics
module. Abstract virtual functions are declared for replotting
and selecting items.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
31da037701 statistics: implement axes
Implement five kinds of axes:
 - ValueAxis: a standard axis for plotting numerical linear data.
 - CountAxis: a ValueAxis for plotting counts of dives.
 - CategoryAxis: an axis for plotting discrete variables without
   any notion of distance.
 - HistogramAxis: an axis for plotting bins with a numeric value.
 - DateAxis: a HistogramAxis that formats dates.

The axes derive from a common virtual base class that defines
a small interface, notably, returning the minimum and maximum
displayed value and redrawing the axis.

The mapping and painting is performed by QtCharts' axes. On
the one hand, using QtCharts turned out to be too inflexible.
On the other hand it allowed us to quickly prototype the charts.
Ultimately, we should do our own drawing of the axis.

As a testament to the inflexibility, QtCharts' axes do not
allow for repeated labels is needed for quarter-based date
charts (year, Q2, Q3, Q4, year, Q2, Q3, ...). Therefore the
code disambiguates labels by adding unicode zero-width spaces.
Wonderful.

When omitting labels due to space reasons, the histogram
axis attempts to show "preferred" labels. In the quarter
example above, it tries to show full years.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
3b26f6a767 statistics: add a header file defining z-values
z-values determine the order in which objects on the chart are
painted. To reduce chaos, collect all z-values in a header file.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-02 11:04:03 -08:00
Berthold Stoeger
3a65b0f37f statistics: implement a legend box
For some chart (e.g. pie charts or stacked bar charts), we want
to display a legend. QtCharts' legend interface happens to be
private and therefore is of no use.

This introduces a legend box which is implemented using
QGraphicItems, which can be placed on top of QCharts. It's very
unfancy, but works for now. If there are too many items, not
all are shown. Currently, the legend is configured to fill
at most half of the width and half of the height of the chart.
This might need some optimization.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-01 21:10:10 +01:00
Dirk Hohndel
7339dc28ae statistics: add color-related functions
Add a source file and a header file, which implement the color
scheme used by the statistics module.

Besides a few color constants, the centerpiece is a
function that returns the color representing a bin and
an appropriate label color. It picks a roughly equi-distant
set of colors out of an already balanced set of 50 candidate
colors. And it also picks white as text color when adding a
label to a segment with a dark color.

The color list was created using a tool by Gregor Aisch that
is available on GitHub as https://github.com/gka/palettes to
create multi-hued, multi-stop color scales that are safe for
color blind people.

This commit contains code from three authors.
Dirk (main author): adaptive color scheme.
Willem: Colors of single-bin charts and lines.
Berthold: Infrastructure.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: willemferguson <willemferguson@zoology.up.ac.za>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-01 21:10:10 +01:00
Berthold Stoeger
93ba2219ee statistics: add statistic variables
The StatisticVariable class hierarchy encapsulates the concept
of a dive-variable, which can be plotted in charts either as
dependend or independend variable.

There are three types of these variables:
1) discrete: For example dive buddies or suit type.
2) continuous: Has a notion of linear metric - can be
   used as histogram or scatter plot axis.
3) numeric: Like continuous, but allows for operations
   such as calculating the mean or the sum over numerous
   dives.

All variables support binning. The bins are defined per
variable.

Continuous variables can be converted into an arbitrary
double value, which is used to be plotted on a continuous
axis.

Moreover, numeric variables support a number of operations,
which depend on the variable.

Since binning is based on different types, the code is rather
template-heavy. Of course, this could be solved with
unions/variants and runtime-polymorphism, but using templates
was just much quicker. Notably, this uses the CRTP
(curiously recurring template pattern) where a subclass
passes itself as argument to the baseclass. This is a weird
kind of "reverse inheritance".

The StatsTranslations class is a dummy class which will
be used to collect all translations of the statistics
module.

This includes changes by Dirk to fix compilation of the
downloader.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-01 21:10:10 +01:00