1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-01-18 03:42:09 +01:00

Fixed broken graph handling

This commit is contained in:
Martin Tůma 2016-09-19 23:35:04 +02:00
parent e9b32fb582
commit 1957a51570
17 changed files with 130 additions and 147 deletions

View File

@ -71,15 +71,15 @@ void ElevationGraph::loadGraph(const Graph &graph, Type type, PathItem *path)
qreal ascent = 0, descent = 0;
qreal min, max;
if (graph.y.count() < 2) {
if (graph.size() < 2) {
skipColor();
return;
}
max = min = graph.y.at(0);
for (int j = 1; j < graph.y.size(); j++) {
qreal cur = graph.y.at(j);
qreal prev = graph.y.at(j-1);
max = min = graph.at(0).y();
for (int j = 1; j < graph.size(); j++) {
qreal cur = graph.at(j).y();
qreal prev = graph.at(j-1).y();
if (cur > prev)
ascent += cur - prev;

View File

@ -4,7 +4,6 @@
#include "graphtab.h"
class GPX;
class Graph;
class PathItem;
class ElevationGraph : public GraphTab

View File

@ -2,15 +2,30 @@
#define GRAPH_H
#include <QVector>
#include <cmath>
class Graph
enum GraphType {Distance, Time};
class GraphPoint
{
public:
enum Type {Distance, Time};
GraphPoint(qreal s = NAN, qreal t = NAN, qreal y = NAN)
: _s(s), _t(t), _y(y) {}
QVector<qreal> distance;
QVector<qreal> time;
QVector<qreal> y;
qreal s() const {return _s;}
qreal t() const {return _t;}
qreal y() const {return _y;}
void setS(qreal s) {_s = s;}
void setT(qreal t) {_t = t;}
void setY(qreal y) {_y = y;}
private:
qreal _s;
qreal _t;
qreal _y;
};
typedef QVector<GraphPoint> Graph;
#endif // GRAPH_H

View File

@ -33,21 +33,30 @@ static qreal yAtX(const QPainterPath &path, qreal x)
return l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
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)
{
_id = 0;
_pen = QPen(QBrush(Qt::SolidPattern), 0);
_type = Graph::Distance;
_type = Distance;
_distancePath.moveTo(graph.distance.first(), -graph.y.first());
for (int i = 1; i < graph.y.size(); i++)
_distancePath.lineTo(graph.distance.at(i), -graph.y.at(i));
_distancePath.moveTo(graph.first().s(), -graph.first().y());
for (int i = 1; i < graph.size(); i++)
_distancePath.lineTo(graph.at(i).s(), -graph.at(i).y());
if (!graph.time.isEmpty()) {
_timePath.moveTo(graph.time.first(), -graph.y.first());
for (int i = 1; i < graph.y.size(); i++)
_timePath.lineTo(graph.time.at(i), -graph.y.at(i));
if (hasTime(graph)) {
_timePath.moveTo(graph.first().t(), -graph.first().y());
for (int i = 1; i < graph.size(); i++)
_timePath.lineTo(graph.at(i).t(), -graph.at(i).y());
}
}
@ -58,7 +67,7 @@ void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
Q_UNUSED(widget);
painter->setPen(_pen);
painter->drawPath((_type == Graph::Distance) ? _distancePath : _timePath);
painter->drawPath((_type == Distance) ? _distancePath : _timePath);
}
void GraphItem::setColor(const QColor &color)
@ -69,7 +78,7 @@ void GraphItem::setColor(const QColor &color)
qreal GraphItem::yAtX(qreal x)
{
return ::yAtX((_type == Graph::Distance) ? _distancePath : _timePath, x);
return ::yAtX((_type == Distance) ? _distancePath : _timePath, x);
}
qreal GraphItem::distanceAtTime(qreal time)
@ -103,7 +112,7 @@ qreal GraphItem::distanceAtTime(qreal time)
void GraphItem::emitSliderPositionChanged(qreal pos)
{
if (_type == Graph::Time) {
if (_type == Time) {
if (!_timePath.isEmpty()) {
if (pos <= _timePath.elementAt(_timePath.elementCount() - 1).x)
emit sliderPositionChanged(distanceAtTime(pos));

View File

@ -13,12 +13,12 @@ public:
GraphItem(const Graph &graph, QGraphicsItem *parent = 0);
QRectF boundingRect() const
{return (_type == Graph::Distance) ? _distancePath.boundingRect()
{return (_type == Distance) ? _distancePath.boundingRect()
: _timePath.boundingRect();}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
void setGraphType(Graph::Type type) {_type = type;}
void setGraphType(GraphType type) {_type = type;}
int id() const {return _id;}
void setId(int id) {_id = id;}
void setColor(const QColor &color);
@ -36,7 +36,7 @@ private:
int _id;
QPen _pen;
QPainterPath _distancePath, _timePath;
Graph::Type _type;
GraphType _type;
};
#endif // GRAPHITEM_H

View File

@ -59,7 +59,7 @@ GraphView::GraphView(QWidget *parent)
_sliderPos = 0;
_units = Metric;
_graphType = Graph::Distance;
_graphType = Distance;
setGraphType(_graphType);
setUnits(_units);
@ -105,7 +105,7 @@ void GraphView::setYUnits(const QString &units)
void GraphView::setXUnits()
{
if (_graphType == Graph::Distance) {
if (_graphType == Distance) {
if (_units == Metric) {
if (bounds().width() < KMINM) {
_xUnits = tr("m");
@ -145,7 +145,7 @@ void GraphView::setUnits(Units units)
setXUnits();
}
void GraphView::setGraphType(Graph::Type type)
void GraphView::setGraphType(GraphType type)
{
_graphType = type;
_bounds = QRectF();
@ -155,7 +155,7 @@ void GraphView::setGraphType(Graph::Type type)
updateBounds(_graphs.at(i)->boundingRect());
}
if (type == Graph::Distance)
if (type == Distance)
_xLabel = tr("Distance");
else
_xLabel = tr("Time");
@ -166,7 +166,7 @@ void GraphView::setGraphType(Graph::Type type)
void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
{
if (graph.y.size() < 2)
if (graph.size() < 2)
return;
GraphItem *gi = new GraphItem(graph);

View File

@ -45,7 +45,7 @@ public:
void clear();
void showGraph(bool show, int id = 0);
void setGraphType(Graph::Type type);
void setGraphType(GraphType type);
void setUnits(Units units);
const QString &yLabel() const {return _yLabel;}
@ -113,7 +113,7 @@ private:
Palette _palette;
Units _units;
Graph::Type _graphType;
GraphType _graphType;
};
#endif // GRAPHVIEW_H

View File

@ -1096,13 +1096,13 @@ void GUI::setImperialUnits()
void GUI::setDistanceGraph()
{
for (int i = 0; i <_tabs.count(); i++)
_tabs.at(i)->setGraphType(Graph::Distance);
_tabs.at(i)->setGraphType(Distance);
}
void GUI::setTimeGraph()
{
for (int i = 0; i <_tabs.count(); i++)
_tabs.at(i)->setGraphType(Graph::Time);
_tabs.at(i)->setGraphType(Time);
}
void GUI::next()
@ -1208,7 +1208,7 @@ void GUI::writeSettings()
settings.beginGroup(GRAPH_SETTINGS_GROUP);
settings.setValue(SHOW_GRAPHS_SETTING, _showGraphsAction->isChecked());
settings.setValue(GRAPH_TYPE_SETTING, _timeGraphAction->isChecked()
? Graph::Time : Graph::Distance);
? Time : Distance);
settings.endGroup();
settings.beginGroup(POI_SETTINGS_GROUP);
@ -1280,8 +1280,7 @@ void GUI::readSettings()
showGraphs(false);
else
_showGraphsAction->setChecked(true);
if (settings.value(GRAPH_TYPE_SETTING, Graph::Distance).toInt()
== Graph::Time) {
if (settings.value(GRAPH_TYPE_SETTING, Distance).toInt() == Time) {
setTimeGraph();
_timeGraphAction->setChecked(true);
} else

View File

@ -30,14 +30,14 @@ void HeartRateGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
const Graph &graph = gpx.tracks().at(i)->heartRate();
qreal sum = 0, w = 0;
if (graph.y.count() < 2) {
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.y.size(); j++) {
qreal ds = graph.distance.at(j) - graph.distance.at(j-1);
sum += graph.y.at(j) * ds;
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(gpx.tracks().at(i)->distance(), sum/w));

View File

@ -5,10 +5,10 @@ Route::Route(const QVector<Waypoint> &data) : _data(data)
{
qreal dist = 0;
_dd.append(dist);
_distance.append(dist);
for (int i = 1; i < data.count(); i++) {
dist += llDistance(data.at(i).coordinates(), data.at(i-1).coordinates());
_dd.append(dist);
_distance.append(dist);
}
}
@ -18,14 +18,13 @@ Graph Route::elevation() const
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation())
graph.y.append(_data.at(i).elevation() - _data.at(i).geoidHeight());
graph.distance = _dd;
graph.append(GraphPoint(_distance.at(i), NAN,
_data.at(i).elevation() - _data.at(i).geoidHeight()));
return graph;
}
qreal Route::distance() const
{
return (_dd.isEmpty()) ? 0 : _dd.last();
return (_distance.isEmpty()) ? 0 : _distance.last();
}

View File

@ -15,11 +15,11 @@ public:
qreal distance() const;
bool isNull() const {return (_dd.count() < 2);}
bool isNull() const {return (_data.count() < 2);}
private:
const QVector<Waypoint> &_data;
QVector<qreal> _dd;
QVector<qreal> _distance;
};
#endif // ROUTE_H

View File

@ -29,7 +29,7 @@ void SpeedGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
{
for (int i = 0; i < gpx.tracks().count(); i++) {
const Graph &graph = gpx.tracks().at(i)->speed();
if (graph.y.count() < 2) {
if (graph.size() < 2) {
skipColor();
continue;
}

View File

@ -32,14 +32,14 @@ void TemperatureGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
const Graph &graph = gpx.tracks().at(i)->temperature();
qreal sum = 0, w = 0;
if (graph.y.count() < 2) {
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.y.size(); j++) {
qreal ds = graph.distance.at(j) - graph.distance.at(j-1);
sum += graph.y.at(j) * ds;
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(gpx.tracks().at(i)->distance(), sum/w));

View File

@ -8,39 +8,40 @@
#define WINDOW_HE 11
#define WINDOW_HF 3
static bool lt(qreal v1, qreal v2)
static bool lt(const GraphPoint &v1, const GraphPoint &v2)
{
return v1 < v2;
return v1.y() < v2.y();
}
static qreal median(QVector<qreal> v)
static qreal median(QVector<GraphPoint> v)
{
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2);
return v.at(v.size() / 2).y();
}
static qreal MAD(QVector<qreal> v, qreal m)
static qreal MAD(QVector<GraphPoint> v, qreal m)
{
for (int i = 0; i < v.size(); i++)
v[i] = (qAbs(v.at(i) - m));
v[i].setY(qAbs(v.at(i).y() - m));
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2);
return v.at(v.size() / 2).y();
}
static QVector<qreal> eliminate(const QVector<qreal> &v, int window)
static QVector<GraphPoint> eliminate(const QVector<GraphPoint> &v, int window)
{
QList<int> rm;
QVector<qreal> ret;
QVector<GraphPoint> ret;
qreal m, M;
if (v.size() < window)
return QVector<qreal>(v);
return QVector<GraphPoint>(v);
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) - m)) / M) > 3.5)
if (qAbs((0.6745 * (v.at(i).y() - m)) / M) > 3.5)
rm.append(i);
}
@ -55,26 +56,26 @@ static QVector<qreal> eliminate(const QVector<qreal> &v, int window)
return ret;
}
static QVector<qreal> filter(const QVector<qreal> &v, int window)
static QVector<GraphPoint> filter(const QVector<GraphPoint> &v, int window)
{
qreal acc = 0;
QVector<qreal> ret;
QVector<GraphPoint> ret;
if (v.size() < window)
return QVector<qreal>(v);
return QVector<GraphPoint>(v);
for (int i = 0; i < window; i++)
acc += v.at(i);
acc += v.at(i).y();
for (int i = 0; i <= window/2; i++)
ret.append(acc/window);
ret.append(GraphPoint(v.at(i).s(), v.at(i).t(), acc/window));
for (int i = window/2 + 1; i < v.size() - window/2; i++) {
acc += v.at(i + window/2) - v.at(i - (window/2 + 1));
ret.append(acc/window);
acc += v.at(i + window/2).y() - v.at(i - (window/2 + 1)).y();
ret.append(GraphPoint(v.at(i).s(), v.at(i).t(), acc/window));
}
for (int i = v.size() - window/2; i < v.size(); i++)
ret.append(acc/window);
ret.append(GraphPoint(v.at(i).s(), v.at(i).t(), acc/window));
return ret;
}
@ -82,112 +83,93 @@ static QVector<qreal> filter(const QVector<qreal> &v, int window)
Track::Track(const QVector<Trackpoint> &data) : _data(data)
{
qreal dist = 0;
qint64 time;
_dd.append(0);
_td.append(0);
_distance.append(0);
_time.append(0);
for (int i = 1; i < data.count(); i++) {
dist += llDistance(data.at(i).coordinates(), data.at(i-1).coordinates());
_dd.append(dist);
if (data.first().hasTimestamp() && data.at(i).hasTimestamp()) {
time = _data.first().timestamp().msecsTo(_data.at(i).timestamp());
_td.append((qreal)time / 1000.0);
}
}
_distance.append(dist);
if (_dd.size() != _td.size())
_td.clear();
if (data.first().hasTimestamp() && data.at(i).hasTimestamp())
_time.append(_data.first().timestamp().msecsTo(
_data.at(i).timestamp()) / 1000.0);
else
_time.append(NAN);
}
}
Graph Track::elevation() const
{
Graph ret;
QVector<qreal> raw;
QVector<GraphPoint> raw;
if (!_data.size())
return ret;
return raw;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation())
raw.append(_data.at(i).elevation() - _data.at(i).geoidHeight());
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).elevation() - _data.at(i).geoidHeight()));
ret.y = filter(raw, WINDOW_EF);
ret.distance = _dd;
ret.time = _td;
return ret;
return filter(raw, WINDOW_EF);
}
Graph Track::speed() const
{
Graph ret;
QVector<GraphPoint> raw;
qreal v, ds, dt;
QVector<qreal> raw;
if (!_data.size())
return ret;
return raw;
raw.append(0);
raw.append(GraphPoint(_distance.at(0), _time.at(0), 0));
for (int i = 1; i < _data.size(); i++) {
if (_data.at(i).hasSpeed())
v = _data.at(i).speed();
else if (_data.at(i).hasTimestamp()) {
dt = _td.at(i) - _td.at(i-1);
else if (_data.at(i).hasTimestamp() && _data.at(i-1).hasTimestamp()) {
dt = _time.at(i) - _time.at(i-1);
if (!dt)
continue;
ds = _dd.at(i) - _dd.at(i-1);
ds = _distance.at(i) - _distance.at(i-1);
v = ds / dt;
} else
continue;
raw.append(v);
raw.append(GraphPoint(_distance.at(i), _time.at(i), v));
}
ret.y = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
ret.distance = _dd;
ret.time = _td;
return ret;
return filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
}
Graph Track::heartRate() const
{
Graph ret;
QVector<qreal> raw;
QVector<GraphPoint> raw;
if (!_data.size())
return ret;
return raw;
for (int i = 0; i < _data.count(); i++)
if (_data.at(i).hasHeartRate())
raw.append(_data.at(i).heartRate());
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).heartRate()));
ret.y = filter(eliminate(raw, WINDOW_HE), WINDOW_HF);
ret.distance = _dd;
ret.time = _td;
return ret;
return filter(eliminate(raw, WINDOW_HE), WINDOW_HF);
}
Graph Track::temperature() const
{
Graph ret;
QVector<GraphPoint> raw;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasTemperature())
ret.y.append(_data.at(i).temperature());
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).temperature()));
ret.distance = _dd;
ret.time = _td;
return ret;
return Graph(raw);
}
qreal Track::distance() const
{
return (_dd.isEmpty()) ? 0 : _dd.last();
return (_distance.isEmpty()) ? 0 : _distance.last();
}
qreal Track::time() const

View File

@ -21,12 +21,12 @@ public:
qreal time() const;
QDateTime date() const;
bool isNull() const {return (_dd.count() < 2);}
bool isNull() const {return (_data.size() < 2);}
private:
const QVector<Trackpoint> &_data;
QVector<qreal> _dd;
QVector<qreal> _td;
QVector<qreal> _distance;
QVector<qreal> _time;
};
#endif // TRACK_H

View File

@ -45,7 +45,6 @@ TrackView::TrackView(QWidget *parent)
_showRouteWaypoints = true;
_plot = false;
//_markerPos = 0;
}
TrackView::~TrackView()
@ -68,7 +67,6 @@ PathItem *TrackView::addTrack(const Track &track)
ti->setScale(1.0/_scale);
ti->setColor(_palette.color());
ti->setVisible(_showTracks);
//ti->moveMarker(_markerPos);
_scene->addItem(ti);
return ti;
@ -90,7 +88,6 @@ PathItem *TrackView::addRoute(const Route &route)
ri->setVisible(_showRoutes);
ri->showWaypoints(_showRouteWaypoints);
ri->showWaypointLabels(_showWaypointLabels);
//ri->moveMarker(_markerPos);
_scene->addItem(ri);
return ri;
@ -490,23 +487,8 @@ void TrackView::clear()
_scale = mapScale(_zoom);
_scene->setSceneRect(QRectF());
//_markerPos = 0;
}
/*
void TrackView::movePositionMarker(qreal val)
{
_markerPos = val;
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->moveMarker(val);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->moveMarker(val);
}
*/
void TrackView::showTracks(bool show)
{
_showTracks = show;

View File

@ -44,7 +44,6 @@ public:
int waypointCount() const {return _waypoints.count();}
public slots:
//void movePositionMarker(qreal val);
void redraw();
void setPOIOverlap(bool overlap);
@ -104,7 +103,6 @@ private:
bool _showRouteWaypoints;
bool _plot;
//qreal _markerPos;
};
#endif // TRACKVIEW_H