mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-02-17 16:20:48 +01:00
Redesigned track data filtering
This commit is contained in:
parent
584245785e
commit
b9098c0dc5
@ -64,7 +64,9 @@ HEADERS += src/config.h \
|
||||
src/trackdata.h \
|
||||
src/routedata.h \
|
||||
src/fitparser.h \
|
||||
src/format.h
|
||||
src/format.h \
|
||||
src/path.h \
|
||||
src/assert.h
|
||||
SOURCES += src/main.cpp \
|
||||
src/gui.cpp \
|
||||
src/poi.cpp \
|
||||
|
17
src/graph.h
17
src/graph.h
@ -27,7 +27,22 @@ private:
|
||||
qreal _y;
|
||||
};
|
||||
|
||||
typedef QVector<GraphPoint> Graph;
|
||||
class Graph : public QVector<GraphPoint>
|
||||
{
|
||||
public:
|
||||
Graph() : QVector<GraphPoint>() {_time = true;}
|
||||
void append(const GraphPoint &p)
|
||||
{
|
||||
if (std::isnan(p.t()))
|
||||
_time = false;
|
||||
QVector<GraphPoint>::append(p);
|
||||
}
|
||||
|
||||
bool hasTime() const {return _time;}
|
||||
|
||||
private:
|
||||
bool _time;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(GraphPoint, Q_PRIMITIVE_TYPE);
|
||||
|
||||
|
@ -5,15 +5,6 @@
|
||||
#define GRAPH_WIDTH 1
|
||||
#define HOVER_WIDTH 2
|
||||
|
||||
static bool hasTime(const Graph &graph)
|
||||
{
|
||||
for (int i = 0; i < graph.count(); i++)
|
||||
if (std::isnan(graph.at(i).t()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
|
||||
: QGraphicsObject(parent)
|
||||
{
|
||||
@ -24,7 +15,7 @@ GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
|
||||
_type = Distance;
|
||||
_graph = graph;
|
||||
_sx = 1.0; _sy = 1.0;
|
||||
_time = hasTime(_graph);
|
||||
_time = _graph.hasTime();
|
||||
|
||||
updatePath();
|
||||
updateBounds();
|
||||
|
9
src/path.h
Normal file
9
src/path.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef PATH_H
|
||||
#define PATH_H
|
||||
|
||||
#include <QVector>
|
||||
#include "coordinates.h"
|
||||
|
||||
typedef QVector<Coordinates> Path;
|
||||
|
||||
#endif // PATH_H
|
146
src/track.cpp
146
src/track.cpp
@ -1,67 +1,53 @@
|
||||
#include "track.h"
|
||||
|
||||
|
||||
#define WINDOW_OE 31
|
||||
|
||||
#define WINDOW_EF 3
|
||||
#define WINDOW_SE 11
|
||||
#define WINDOW_SF 7
|
||||
#define WINDOW_HE 11
|
||||
#define WINDOW_HF 3
|
||||
|
||||
|
||||
static bool lt(const GraphPoint &v1, const GraphPoint &v2)
|
||||
static qreal median(QVector<qreal> v)
|
||||
{
|
||||
return v1.y() < v2.y();
|
||||
qSort(v.begin(), v.end());
|
||||
return v.at(v.size() / 2);
|
||||
}
|
||||
|
||||
static qreal median(QVector<GraphPoint> v)
|
||||
{
|
||||
qSort(v.begin(), v.end(), lt);
|
||||
return v.at(v.size() / 2).y();
|
||||
}
|
||||
|
||||
static qreal MAD(QVector<GraphPoint> v, qreal m)
|
||||
static qreal MAD(QVector<qreal> v, qreal m)
|
||||
{
|
||||
for (int i = 0; i < v.size(); i++)
|
||||
v[i].setY(qAbs(v.at(i).y() - m));
|
||||
qSort(v.begin(), v.end(), lt);
|
||||
return v.at(v.size() / 2).y();
|
||||
v[i] = qAbs(v.at(i) - m);
|
||||
qSort(v.begin(), v.end());
|
||||
return v.at(v.size() / 2);
|
||||
}
|
||||
|
||||
static QVector<GraphPoint> eliminate(const QVector<GraphPoint> &v, int window)
|
||||
static QSet<int> eliminate(const QVector<qreal> &v, int window)
|
||||
{
|
||||
QList<int> rm;
|
||||
QVector<GraphPoint> ret;
|
||||
QSet<int> rm;
|
||||
qreal m, M;
|
||||
|
||||
|
||||
if (v.size() < window)
|
||||
return QVector<GraphPoint>(v);
|
||||
return rm;
|
||||
|
||||
for (int i = window/2; i < v.size() - window/2; i++) {
|
||||
m = median(v.mid(i - window/2, window));
|
||||
M = MAD(v.mid(i - window/2, window), m);
|
||||
if (qAbs((0.6745 * (v.at(i).y() - m)) / M) > 3.5)
|
||||
rm.append(i);
|
||||
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 3.5)
|
||||
rm.insert(i);
|
||||
}
|
||||
|
||||
QList<int>::const_iterator it = rm.begin();
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (it == rm.end() || *it != i)
|
||||
ret.append(v.at(i));
|
||||
else
|
||||
it++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return rm;
|
||||
}
|
||||
|
||||
static QVector<GraphPoint> filter(const QVector<GraphPoint> &v, int window)
|
||||
static Graph filter(const Graph &v, int window)
|
||||
{
|
||||
qreal acc = 0;
|
||||
QVector<GraphPoint> ret;
|
||||
Graph ret;
|
||||
|
||||
if (v.size() < window)
|
||||
return QVector<GraphPoint>(v);
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < window; i++)
|
||||
acc += v.at(i).y();
|
||||
@ -81,94 +67,128 @@ static QVector<GraphPoint> filter(const QVector<GraphPoint> &v, int window)
|
||||
|
||||
Track::Track(const TrackData &data) : _data(data)
|
||||
{
|
||||
qreal dist = 0;
|
||||
qreal dt, ds, total;
|
||||
|
||||
|
||||
_distance.append(0);
|
||||
_time.append(0);
|
||||
_distance.append(0);
|
||||
_speed.append(0);
|
||||
|
||||
for (int i = 1; i < data.count(); i++) {
|
||||
dist += data.at(i).coordinates().distanceTo(data.at(i-1).coordinates());
|
||||
_distance.append(dist);
|
||||
ds = data.at(i).coordinates().distanceTo(data.at(i-1).coordinates());
|
||||
_distance.append(ds);
|
||||
|
||||
if (data.first().hasTimestamp() && data.at(i).hasTimestamp())
|
||||
_time.append(_data.first().timestamp().msecsTo(
|
||||
_data.at(i).timestamp()) / 1000.0);
|
||||
else
|
||||
_time.append(NAN);
|
||||
|
||||
if (std::isnan(_time.at(i)) || std::isnan(_time.at(i-1)))
|
||||
_speed.append(NAN);
|
||||
else {
|
||||
dt = _time.at(i) - _time.at(i-1);
|
||||
if (!dt) {
|
||||
_speed.append(_speed.at(i-1));
|
||||
continue;
|
||||
}
|
||||
_speed.append(ds / dt);
|
||||
}
|
||||
}
|
||||
|
||||
_outliers = eliminate(_speed, WINDOW_OE);
|
||||
|
||||
total = 0;
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
total += _distance.at(i);
|
||||
_distance[i] = total;
|
||||
}
|
||||
}
|
||||
|
||||
Graph Track::elevation() const
|
||||
{
|
||||
QVector<GraphPoint> raw;
|
||||
Graph raw;
|
||||
|
||||
if (!_data.size())
|
||||
return raw;
|
||||
|
||||
for (int i = 0; i < _data.size(); i++)
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
|
||||
if (_data.at(i).hasElevation())
|
||||
raw.append(GraphPoint(_distance.at(i), _time.at(i),
|
||||
_data.at(i).elevation()));
|
||||
}
|
||||
|
||||
return filter(raw, WINDOW_EF);
|
||||
}
|
||||
|
||||
Graph Track::speed() const
|
||||
{
|
||||
QVector<GraphPoint> raw;
|
||||
qreal v, ds, dt;
|
||||
Graph raw;
|
||||
qreal v;
|
||||
|
||||
if (!_data.size())
|
||||
return raw;
|
||||
|
||||
raw.append(GraphPoint(_distance.at(0), _time.at(0), 0));
|
||||
for (int i = 1; i < _data.size(); i++) {
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
|
||||
if (_data.at(i).hasSpeed())
|
||||
v = _data.at(i).speed();
|
||||
else if (_data.at(i).hasTimestamp() && _data.at(i-1).hasTimestamp()) {
|
||||
dt = _time.at(i) - _time.at(i-1);
|
||||
if (!dt)
|
||||
continue;
|
||||
ds = _distance.at(i) - _distance.at(i-1);
|
||||
v = ds / dt;
|
||||
} else
|
||||
else if (!std::isnan(_speed.at(i)))
|
||||
v = _speed.at(i);
|
||||
else
|
||||
continue;
|
||||
|
||||
raw.append(GraphPoint(_distance.at(i), _time.at(i), v));
|
||||
}
|
||||
|
||||
return filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
|
||||
return filter(raw, WINDOW_SF);
|
||||
}
|
||||
|
||||
Graph Track::heartRate() const
|
||||
{
|
||||
QVector<GraphPoint> raw;
|
||||
Graph raw;
|
||||
|
||||
if (!_data.size())
|
||||
return raw;
|
||||
|
||||
for (int i = 0; i < _data.count(); i++)
|
||||
for (int i = 0; i < _data.count(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
|
||||
if (_data.at(i).hasHeartRate())
|
||||
raw.append(GraphPoint(_distance.at(i), _time.at(i),
|
||||
_data.at(i).heartRate()));
|
||||
}
|
||||
|
||||
return filter(eliminate(raw, WINDOW_HE), WINDOW_HF);
|
||||
return filter(raw, WINDOW_HF);
|
||||
}
|
||||
|
||||
Graph Track::temperature() const
|
||||
{
|
||||
QVector<GraphPoint> raw;
|
||||
Graph raw;
|
||||
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < _data.size(); i++)
|
||||
if (_data.at(i).hasTemperature())
|
||||
raw.append(GraphPoint(_distance.at(i), _time.at(i),
|
||||
_data.at(i).temperature()));
|
||||
}
|
||||
|
||||
return Graph(raw);
|
||||
}
|
||||
|
||||
qreal Track::distance() const
|
||||
{
|
||||
return (_distance.isEmpty()) ? 0 : _distance.last();
|
||||
return _distance.isEmpty() ? 0 : _distance.last();
|
||||
}
|
||||
|
||||
qreal Track::time() const
|
||||
@ -181,3 +201,17 @@ QDateTime Track::date() const
|
||||
{
|
||||
return (_data.size()) ? _data.first().timestamp() : QDateTime();
|
||||
}
|
||||
|
||||
Path Track::track() const
|
||||
{
|
||||
Path ret;
|
||||
|
||||
for (int i = 0; i < _data.size(); i++) {
|
||||
if (_outliers.contains(i))
|
||||
continue;
|
||||
|
||||
ret.append(_data.at(i).coordinates());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
11
src/track.h
11
src/track.h
@ -2,9 +2,11 @@
|
||||
#define TRACK_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
#include <QDateTime>
|
||||
#include "trackdata.h"
|
||||
#include "graph.h"
|
||||
#include "path.h"
|
||||
|
||||
|
||||
class Track
|
||||
@ -12,7 +14,7 @@ class Track
|
||||
public:
|
||||
Track(const TrackData &data);
|
||||
|
||||
const TrackData &track() const {return _data;}
|
||||
Path track() const;
|
||||
Graph elevation() const;
|
||||
Graph speed() const;
|
||||
Graph heartRate() const;
|
||||
@ -22,12 +24,19 @@ public:
|
||||
qreal time() const;
|
||||
QDateTime date() const;
|
||||
|
||||
const QString &name() const {return _data.name();}
|
||||
const QString &description() const {return _data.description();}
|
||||
|
||||
bool isNull() const {return (_data.size() < 2);}
|
||||
|
||||
private:
|
||||
const TrackData &_data;
|
||||
|
||||
QVector<qreal> _distance;
|
||||
QVector<qreal> _time;
|
||||
QVector<qreal> _speed;
|
||||
|
||||
QSet<int> _outliers;
|
||||
};
|
||||
|
||||
#endif // TRACK_H
|
||||
|
@ -30,20 +30,20 @@ TrackItem::TrackItem(const Track &track, QGraphicsItem *parent)
|
||||
: PathItem(parent)
|
||||
{
|
||||
QPointF p;
|
||||
const TrackData &t = track.track();
|
||||
QVector<Coordinates> t = track.track();
|
||||
Q_ASSERT(t.count() >= 2);
|
||||
|
||||
p = t.first().coordinates().toMercator();
|
||||
p = t.first().toMercator();
|
||||
_path.moveTo(QPointF(p.x(), -p.y()));
|
||||
for (int i = 1; i < t.size(); i++) {
|
||||
p = t.at(i).coordinates().toMercator();
|
||||
p = t.at(i).toMercator();
|
||||
_path.lineTo(QPointF(p.x(), -p.y()));
|
||||
}
|
||||
|
||||
updateShape();
|
||||
|
||||
_name = t.name();
|
||||
_desc = t.description();
|
||||
_name = track.name();
|
||||
_desc = track.description();
|
||||
_date = track.date();
|
||||
_distance = track.distance();
|
||||
_time = track.time();
|
||||
|
Loading…
x
Reference in New Issue
Block a user