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