diff --git a/gpxsee.pro b/gpxsee.pro index 87c734b5..76be18b7 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -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 \ diff --git a/src/graph.h b/src/graph.h index ed9efa67..d6880341 100644 --- a/src/graph.h +++ b/src/graph.h @@ -27,7 +27,22 @@ private: qreal _y; }; -typedef QVector Graph; +class Graph : public QVector +{ +public: + Graph() : QVector() {_time = true;} + void append(const GraphPoint &p) + { + if (std::isnan(p.t())) + _time = false; + QVector::append(p); + } + + bool hasTime() const {return _time;} + +private: + bool _time; +}; Q_DECLARE_TYPEINFO(GraphPoint, Q_PRIMITIVE_TYPE); diff --git a/src/graphitem.cpp b/src/graphitem.cpp index e2d68c18..dce41da2 100644 --- a/src/graphitem.cpp +++ b/src/graphitem.cpp @@ -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(); diff --git a/src/path.h b/src/path.h new file mode 100644 index 00000000..2dd9f598 --- /dev/null +++ b/src/path.h @@ -0,0 +1,9 @@ +#ifndef PATH_H +#define PATH_H + +#include +#include "coordinates.h" + +typedef QVector Path; + +#endif // PATH_H diff --git a/src/track.cpp b/src/track.cpp index 84ffcaaf..f1f010dd 100644 --- a/src/track.cpp +++ b/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 v) { - return v1.y() < v2.y(); + qSort(v.begin(), v.end()); + return v.at(v.size() / 2); } -static qreal median(QVector v) -{ - qSort(v.begin(), v.end(), lt); - return v.at(v.size() / 2).y(); -} - -static qreal MAD(QVector v, qreal m) +static qreal MAD(QVector 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 eliminate(const QVector &v, int window) +static QSet eliminate(const QVector &v, int window) { - QList rm; - QVector ret; + QSet rm; qreal m, M; if (v.size() < window) - return QVector(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::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 filter(const QVector &v, int window) +static Graph filter(const Graph &v, int window) { qreal acc = 0; - QVector ret; + Graph ret; if (v.size() < window) - return QVector(v); + return ret; for (int i = 0; i < window; i++) acc += v.at(i).y(); @@ -81,94 +67,128 @@ static QVector filter(const QVector &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 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 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 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 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; +} diff --git a/src/track.h b/src/track.h index ce5da71f..2205835f 100644 --- a/src/track.h +++ b/src/track.h @@ -2,9 +2,11 @@ #define TRACK_H #include +#include #include #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 _distance; QVector _time; + QVector _speed; + + QSet _outliers; }; #endif // TRACK_H diff --git a/src/trackitem.cpp b/src/trackitem.cpp index fe6bf77f..e09fde74 100644 --- a/src/trackitem.cpp +++ b/src/trackitem.cpp @@ -30,20 +30,20 @@ TrackItem::TrackItem(const Track &track, QGraphicsItem *parent) : PathItem(parent) { QPointF p; - const TrackData &t = track.track(); + QVector 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();