1
0
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:
Martin Tůma 2016-11-05 17:33:29 +01:00
parent 584245785e
commit b9098c0dc5
7 changed files with 134 additions and 74 deletions

View File

@ -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 \

View File

@ -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);

View File

@ -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
View File

@ -0,0 +1,9 @@
#ifndef PATH_H
#define PATH_H
#include <QVector>
#include "coordinates.h"
typedef QVector<Coordinates> Path;
#endif // PATH_H

View File

@ -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;
}

View File

@ -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

View File

@ -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();