diff --git a/gpxsee.pro b/gpxsee.pro index e447f6f0..facd3dcb 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -82,6 +82,7 @@ HEADERS += src/common/config.h \ src/GUI/searchpointer.h \ src/GUI/mapview.h \ src/GUI/font.h \ + src/GUI/areaitem.h \ src/map/projection.h \ src/map/ellipsoid.h \ src/map/datum.h \ @@ -131,6 +132,7 @@ HEADERS += src/common/config.h \ src/map/image.h \ src/map/mbtilesmap.h \ src/map/osm.h \ + src/map/polarstereographic.h \ src/data/graph.h \ src/data/poi.h \ src/data/waypoint.h \ @@ -153,10 +155,8 @@ HEADERS += src/common/config.h \ src/data/locparser.h \ src/data/slfparser.h \ src/data/dem.h \ - src/map/polarstereographic.h \ src/data/polygon.h \ - src/data/area.h \ - src/GUI/areaitem.h + src/data/area.h SOURCES += src/main.cpp \ src/common/coordinates.cpp \ src/common/rectc.cpp \ @@ -206,6 +206,7 @@ SOURCES += src/main.cpp \ src/GUI/powergraphitem.cpp \ src/GUI/gearratiographitem.cpp \ src/GUI/mapview.cpp \ + src/GUI/areaitem.cpp \ src/map/maplist.cpp \ src/map/onlinemap.cpp \ src/map/downloader.cpp \ @@ -250,6 +251,8 @@ SOURCES += src/main.cpp \ src/map/image.cpp \ src/map/mbtilesmap.cpp \ src/map/osm.cpp \ + src/map/polarstereographic.cpp \ + src/map/rectd.cpp \ src/data/data.cpp \ src/data/poi.cpp \ src/data/track.cpp \ @@ -266,10 +269,7 @@ SOURCES += src/main.cpp \ src/data/locparser.cpp \ src/data/slfparser.cpp \ src/data/dem.cpp \ - src/map/polarstereographic.cpp \ - src/map/rectd.cpp \ - src/data/polygon.cpp \ - src/GUI/areaitem.cpp + src/data/polygon.cpp greaterThan(QT_MAJOR_VERSION, 4) { HEADERS += src/data/geojsonparser.h diff --git a/src/GUI/cadencegraphitem.cpp b/src/GUI/cadencegraphitem.cpp index 29bd72a9..690cc8fe 100644 --- a/src/GUI/cadencegraphitem.cpp +++ b/src/GUI/cadencegraphitem.cpp @@ -6,17 +6,6 @@ CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent) { - qreal sum = 0; - _max = graph.first().y(); - - for (int i = 1; i < graph.size(); i++) { - qreal y = graph.at(i).y(); - sum += y * (graph.at(i).s() - graph.at(i-1).s()); - if (y > _max) - _max = y; - } - _avg = sum/graph.last().s(); - setToolTip(toolTip()); } diff --git a/src/GUI/cadencegraphitem.h b/src/GUI/cadencegraphitem.h index 74226fd3..f84689a9 100644 --- a/src/GUI/cadencegraphitem.h +++ b/src/GUI/cadencegraphitem.h @@ -11,13 +11,8 @@ public: CadenceGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent = 0); - qreal max() const {return _max;} - qreal avg() const {return _avg;} - private: QString toolTip() const; - - qreal _avg, _max; }; #endif // CADENCEGRAPHITEM_H diff --git a/src/GUI/elevationgraphitem.cpp b/src/GUI/elevationgraphitem.cpp index de4f9e59..44f74d36 100644 --- a/src/GUI/elevationgraphitem.cpp +++ b/src/GUI/elevationgraphitem.cpp @@ -6,22 +6,22 @@ ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent) { + _min = GraphItem::min(); + _max = GraphItem::max(); + _ascent = _descent = 0; - _min = _max = graph.first().y(); + for (int i = 0; i < graph.size(); i++) { + const GraphSegment &segment = graph.at(i); - for (int j = 1; j < graph.size(); j++) { - qreal cur = graph.at(j).y(); - qreal prev = graph.at(j-1).y(); + for (int j = 1; j < segment.size(); j++) { + qreal cur = segment.at(j).y(); + qreal prev = segment.at(j-1).y(); - if (cur > prev) - _ascent += cur - prev; - if (cur < prev) - _descent += prev - cur; - - if (cur < _min) - _min = cur; - if (cur > _max) - _max = cur; + if (cur > prev) + _ascent += cur - prev; + if (cur < prev) + _descent += prev - cur; + } } setToolTip(toolTip(Metric)); diff --git a/src/GUI/elevationgraphitem.h b/src/GUI/elevationgraphitem.h index ca34e48d..f08d880b 100644 --- a/src/GUI/elevationgraphitem.h +++ b/src/GUI/elevationgraphitem.h @@ -13,8 +13,8 @@ public: qreal ascent() const {return _ascent;} qreal descent() const {return _descent;} - qreal min() const {return _min;} - qreal max() const {return _max;} + qreal max() const {return _min;} + qreal min() const {return _max;} void setUnits(Units units); diff --git a/src/GUI/gearratiographitem.cpp b/src/GUI/gearratiographitem.cpp index 4f44604c..db4e4006 100644 --- a/src/GUI/gearratiographitem.cpp +++ b/src/GUI/gearratiographitem.cpp @@ -8,19 +8,6 @@ GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent), _top(NAN) { qreal val = NAN; - _min = _max = graph.first().y(); - - for (int i = 1; i < graph.size(); i++) { - const GraphPoint &p = graph.at(i); - - qreal val = _map.value(p.y()); - _map.insert(p.y(), val + (p.s() - graph.at(i-1).s())); - - if (p.y() < _min) - _min = p.y(); - if (p.y() > _max) - _max = p.y(); - } for (QMap::const_iterator it = _map.constBegin(); it != _map.constEnd(); ++it) { diff --git a/src/GUI/gearratiographitem.h b/src/GUI/gearratiographitem.h index cf47996c..18f53f6b 100644 --- a/src/GUI/gearratiographitem.h +++ b/src/GUI/gearratiographitem.h @@ -12,8 +12,6 @@ public: GearRatioGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent = 0); - qreal min() const {return _min;} - qreal max() const {return _max;} qreal top() const {return _top;} const QMap &map() const {return _map;} @@ -22,7 +20,7 @@ private: QString toolTip() const; QMap _map; - qreal _top, _min, _max; + qreal _top; }; #endif // GEARRATIOGRAPHITEM_H diff --git a/src/GUI/graphitem.cpp b/src/GUI/graphitem.cpp index 2eacc264..a654463d 100644 --- a/src/GUI/graphitem.cpp +++ b/src/GUI/graphitem.cpp @@ -3,26 +3,17 @@ GraphItem::GraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) - : QGraphicsObject(parent) + : QGraphicsObject(parent), _graph(graph), _type(type) { + Q_ASSERT(_graph.isValid()); + _id = 0; _width = 1; - _pen = QPen(Qt::black, _width); - - _type = type; - _graph = graph; _sx = 1.0; _sy = 1.0; + _time = _graph.hasTime(); - _time = true; - for (int i = 0; i < _graph.size(); i++) { - if (std::isnan(_graph.at(i).t())) { - _time = false; - break; - } - } - - setZValue(1.0); + setZValue(2.0); updatePath(); updateShape(); @@ -89,18 +80,31 @@ void GraphItem::setWidth(int width) updateShape(); } +const GraphSegment *GraphItem::segment(qreal x, GraphType type) const +{ + for (int i = 0; i < _graph.size(); i++) + if (x <= _graph.at(i).last().x(type)) + return &(_graph.at(i)); + + return 0; +} + qreal GraphItem::yAtX(qreal x) { + const GraphSegment *seg = segment(x, _type); + if (!seg) + return NAN; + int low = 0; - int high = _graph.count() - 1; + int high = seg->count() - 1; int mid = 0; - Q_ASSERT(high > low); - Q_ASSERT(x >= _graph.at(low).x(_type) && x <= _graph.at(high).x(_type)); + if (!(x >= seg->at(low).x(_type) && x <= seg->at(high).x(_type))) + return NAN; while (low <= high) { mid = low + ((high - low) / 2); - const GraphPoint &p = _graph.at(mid); + const GraphPoint &p = seg->at(mid); if (p.x(_type) > x) high = mid - 1; else if (p.x(_type) < x) @@ -110,58 +114,56 @@ qreal GraphItem::yAtX(qreal x) } QLineF l; - if (_graph.at(mid).x(_type) < x) - l = QLineF(_graph.at(mid).x(_type), _graph.at(mid).y(), - _graph.at(mid+1).x(_type), _graph.at(mid+1).y()); + if (seg->at(mid).x(_type) < x) + l = QLineF(seg->at(mid).x(_type), seg->at(mid).y(), + seg->at(mid+1).x(_type), seg->at(mid+1).y()); else - l = QLineF(_graph.at(mid-1).x(_type), _graph.at(mid-1).y(), - _graph.at(mid).x(_type), _graph.at(mid).y()); + l = QLineF(seg->at(mid-1).x(_type), seg->at(mid-1).y(), + seg->at(mid).x(_type), seg->at(mid).y()); return -l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y(); } qreal GraphItem::distanceAtTime(qreal time) { + const GraphSegment *seg = segment(time, Time); + if (!seg) + return NAN; + int low = 0; - int high = _graph.count() - 1; + int high = seg->count() - 1; int mid = 0; - Q_ASSERT(high > low); - Q_ASSERT(time >= _graph.at(low).t() && time <= _graph.at(high).t()); + if (!(time >= seg->at(low).t() && time <= seg->at(high).t())) + return NAN; while (low <= high) { mid = low + ((high - low) / 2); - const GraphPoint &p = _graph.at(mid); + const GraphPoint &p = seg->at(mid); if (p.t() > time) high = mid - 1; else if (p.t() < time) low = mid + 1; else - return _graph.at(mid).s(); + return seg->at(mid).s(); } QLineF l; - if (_graph.at(mid).t() < time) - l = QLineF(_graph.at(mid).t(), _graph.at(mid).s(), _graph.at(mid+1).t(), - _graph.at(mid+1).s()); + if (seg->at(mid).t() < time) + l = QLineF(seg->at(mid).t(), seg->at(mid).s(), seg->at(mid+1).t(), + seg->at(mid+1).s()); else - l = QLineF(_graph.at(mid-1).t(), _graph.at(mid-1).s(), - _graph.at(mid).t(), _graph.at(mid).s()); + l = QLineF(seg->at(mid-1).t(), seg->at(mid-1).s(), + seg->at(mid).t(), seg->at(mid).s()); return l.pointAt((time - l.p1().x()) / (l.p2().x() - l.p1().x())).y(); } void GraphItem::emitSliderPositionChanged(qreal pos) { - if (_type == Time) { - if (_time) { - if (pos >= _graph.first().t() && pos <= _graph.last().t()) - emit sliderPositionChanged(distanceAtTime(pos)); - else - emit sliderPositionChanged(NAN); - } else - emit sliderPositionChanged(NAN); - } else + if (_type == Time) + emit sliderPositionChanged(_time ? distanceAtTime(pos) : NAN); + else emit sliderPositionChanged(pos); } @@ -197,9 +199,13 @@ void GraphItem::updatePath() if (_type == Time && !_time) return; - _path.moveTo(_graph.first().x(_type) * _sx, -_graph.first().y() * _sy); - for (int i = 1; i < _graph.size(); i++) - _path.lineTo(_graph.at(i).x(_type) * _sx, -_graph.at(i).y() * _sy); + for (int i = 0; i < _graph.size(); i++) { + const GraphSegment &segment = _graph.at(i); + + _path.moveTo(segment.first().x(_type) * _sx, -segment.first().y() * _sy); + for (int i = 1; i < segment.size(); i++) + _path.lineTo(segment.at(i).x(_type) * _sx, -segment.at(i).y() * _sy); + } } void GraphItem::updateBounds() @@ -211,18 +217,71 @@ void GraphItem::updateBounds() qreal bottom, top, left, right; - QPointF p = QPointF(_graph.first().x(_type), -_graph.first().y()); + QPointF p = QPointF(_graph.first().first().x(_type), + -_graph.first().first().y()); bottom = p.y(); top = p.y(); left = p.x(); right = p.x(); - for (int i = 1; i < _graph.size(); i++) { - p = QPointF(_graph.at(i).x(_type), -_graph.at(i).y()); - bottom = qMax(bottom, p.y()); top = qMin(top, p.y()); - right = qMax(right, p.x()); left = qMin(left, p.x()); + for (int i = 0; i < _graph.size(); i++) { + const GraphSegment &segment = _graph.at(i); + + for (int j = 0; j < segment.size(); j++) { + p = QPointF(segment.at(j).x(_type), -segment.at(j).y()); + bottom = qMax(bottom, p.y()); top = qMin(top, p.y()); + right = qMax(right, p.x()); left = qMin(left, p.x()); + } } _bounds = QRectF(QPointF(left, top), QPointF(right, bottom)); } +qreal GraphItem::max() const +{ + qreal ret = _graph.first().first().y(); + + for (int i = 0; i < _graph.size(); i++) { + const GraphSegment &segment = _graph.at(i); + + for (int j = 0; j < segment.size(); j++) { + qreal y = segment.at(j).y(); + if (y > ret) + ret = y; + } + } + + return ret; +} + +qreal GraphItem::min() const +{ + qreal ret = _graph.first().first().y(); + + for (int i = 0; i < _graph.size(); i++) { + const GraphSegment &segment = _graph.at(i); + + for (int j = 0; j < segment.size(); j++) { + qreal y = segment.at(j).y(); + if (y < ret) + ret = y; + } + } + + return ret; +} + +qreal GraphItem::avg() const +{ + qreal sum = 0; + + for (int i = 0; i < _graph.size(); i++) { + const GraphSegment &segment = _graph.at(i); + + for (int j = 1; j < segment.size(); j++) + sum += segment.at(j).y() * (segment.at(j).s() - segment.at(j-1).s()); + } + + return sum/_graph.last().last().s(); +} + void GraphItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); diff --git a/src/GUI/graphitem.h b/src/GUI/graphitem.h index 3608e6bd..faf6d601 100644 --- a/src/GUI/graphitem.h +++ b/src/GUI/graphitem.h @@ -21,6 +21,10 @@ public: const QRectF &bounds() const {return _bounds;} + qreal max() const; + qreal min() const; + qreal avg() const; + void setScale(qreal sx, qreal sy); void setGraphType(GraphType type); int id() const {return _id;} @@ -46,6 +50,7 @@ private: void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + const GraphSegment *segment(qreal x, GraphType type) const; void updatePath(); void updateShape(); void updateBounds(); diff --git a/src/GUI/graphview.cpp b/src/GUI/graphview.cpp index 0efb9d85..971a0c0c 100644 --- a/src/GUI/graphview.cpp +++ b/src/GUI/graphview.cpp @@ -34,9 +34,9 @@ GraphView::GraphView(QWidget *parent) setBackgroundBrush(QBrush(palette().brush(QPalette::Base))); _xAxis = new AxisItem(AxisItem::X); - _xAxis->setZValue(2.0); + _xAxis->setZValue(1.0); _yAxis = new AxisItem(AxisItem::Y); - _yAxis->setZValue(2.0); + _yAxis->setZValue(1.0); _slider = new SliderItem(); _slider->setZValue(3.0); _sliderInfo = new SliderInfoItem(_slider); diff --git a/src/GUI/heartrategraphitem.cpp b/src/GUI/heartrategraphitem.cpp index 7b5e77d0..8e7a089f 100644 --- a/src/GUI/heartrategraphitem.cpp +++ b/src/GUI/heartrategraphitem.cpp @@ -6,17 +6,6 @@ HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent) { - qreal sum = 0; - _max = graph.first().y(); - - for (int i = 1; i < graph.size(); i++) { - qreal y = graph.at(i).y(); - sum += y * (graph.at(i).s() - graph.at(i-1).s()); - if (y > _max) - _max = y; - } - _avg = sum/graph.last().s(); - setToolTip(toolTip()); } diff --git a/src/GUI/heartrategraphitem.h b/src/GUI/heartrategraphitem.h index b0dcd4f6..2e737a40 100644 --- a/src/GUI/heartrategraphitem.h +++ b/src/GUI/heartrategraphitem.h @@ -11,13 +11,8 @@ public: HeartRateGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent = 0); - qreal max() const {return _max;} - qreal avg() const {return _avg;} - private: QString toolTip() const; - - qreal _avg, _max; }; #endif // HEARTRATEGRAPHITEM_H diff --git a/src/GUI/pathitem.cpp b/src/GUI/pathitem.cpp index 35df8818..032bd769 100644 --- a/src/GUI/pathitem.cpp +++ b/src/GUI/pathitem.cpp @@ -8,19 +8,22 @@ #define GEOGRAPHICAL_MILE 1855.3248 -static unsigned segments(qreal distance) +static inline bool isInvalid(const QPointF &p) +{ + return (std::isnan(p.x()) || std::isnan(p.y())); +} + +static inline unsigned segments(qreal distance) { return ceil(distance / GEOGRAPHICAL_MILE); } PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent) - : QGraphicsObject(parent), _path(path) + : QGraphicsObject(parent), _path(path), _map(map) { - Q_ASSERT(path.count() >= 2); + Q_ASSERT(_path.isValid()); - _map = map; _digitalZoom = 0; - _width = 3; QBrush brush(Qt::SolidPattern); _pen = QPen(brush, _width); @@ -29,8 +32,8 @@ PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent) updateShape(); _marker = new MarkerItem(this); - _marker->setPos(position(_path.at(0).distance())); - _markerDistance = _path.at(0).distance(); + _marker->setPos(position(_path.first().first().distance())); + _markerDistance = _path.first().first().distance(); setCursor(Qt::ArrowCursor); setAcceptHoverEvents(true); @@ -73,23 +76,27 @@ void PathItem::updatePainterPath() { _painterPath = QPainterPath(); - _painterPath.moveTo(_map->ll2xy(_path.first().coordinates())); - for (int i = 1; i < _path.size(); i++) { - const PathPoint &p1 = _path.at(i-1); - const PathPoint &p2 = _path.at(i); - unsigned n = segments(p2.distance() - p1.distance()); + for (int i = 0; i < _path.size(); i++) { + const PathSegment &segment = _path.at(i); + _painterPath.moveTo(_map->ll2xy(segment.first().coordinates())); - if (n > 1) { - GreatCircle gc(p1.coordinates(), p2.coordinates()); - Coordinates last = p1.coordinates(); + for (int j = 1; j < segment.size(); j++) { + const PathPoint &p1 = segment.at(j-1); + const PathPoint &p2 = segment.at(j); + unsigned n = segments(p2.distance() - p1.distance()); - for (unsigned j = 1; j <= n; j++) { - Coordinates c(gc.pointAt(j/(double)n)); - addSegment(last, c); - last = c; - } - } else - addSegment(p1.coordinates(), p2.coordinates()); + if (n > 1) { + GreatCircle gc(p1.coordinates(), p2.coordinates()); + Coordinates last = p1.coordinates(); + + for (unsigned k = 1; k <= n; k++) { + Coordinates c(gc.pointAt(k/(double)n)); + addSegment(last, c); + last = c; + } + } else + addSegment(p1.coordinates(), p2.coordinates()); + } } } @@ -166,35 +173,48 @@ void PathItem::setDigitalZoom(int zoom) updateShape(); } +const PathSegment *PathItem::segment(qreal x) const +{ + for (int i = 0; i < _path.size(); i++) + if (x <= _path.at(i).last().distance()) + return &(_path.at(i)); + + return 0; +} + QPointF PathItem::position(qreal x) const { + const PathSegment *seg = segment(x); + if (!seg) + return QPointF(NAN, NAN); + int low = 0; - int high = _path.count() - 1; + int high = seg->count() - 1; int mid = 0; - Q_ASSERT(high > low); - Q_ASSERT(x >= _path.at(low).distance() && x <= _path.at(high).distance()); + if (!(x >= seg->first().distance() && x <= seg->last().distance())) + return QPointF(NAN, NAN); while (low <= high) { mid = low + ((high - low) / 2); - qreal val = _path.at(mid).distance(); + qreal val = seg->at(mid).distance(); if (val > x) high = mid - 1; else if (val < x) low = mid + 1; else - return _map->ll2xy(_path.at(mid).coordinates()); + return _map->ll2xy(seg->at(mid).coordinates()); } Coordinates c1, c2; qreal p1, p2; - if (_path.at(mid).distance() < x) { - c1 = _path.at(mid).coordinates(); c2 = _path.at(mid+1).coordinates(); - p1 = _path.at(mid).distance(); p2 = _path.at(mid+1).distance(); + if (seg->at(mid).distance() < x) { + c1 = seg->at(mid).coordinates(); c2 = seg->at(mid+1).coordinates(); + p1 = seg->at(mid).distance(); p2 = seg->at(mid+1).distance(); } else { - c1 = _path.at(mid-1).coordinates(); c2 = _path.at(mid).coordinates(); - p1 = _path.at(mid-1).distance(); p2 = _path.at(mid).distance(); + c1 = seg->at(mid-1).coordinates(); c2 = seg->at(mid).coordinates(); + p1 = seg->at(mid-1).distance(); p2 = seg->at(mid).distance(); } unsigned n = segments(p2 - p1); @@ -224,13 +244,15 @@ QPointF PathItem::position(qreal x) const void PathItem::moveMarker(qreal distance) { - if (distance >= _path.first().distance() - && distance <= _path.last().distance()) { - _marker->setVisible(true); - _marker->setPos(position(distance)); - _markerDistance = distance; - } else + QPointF pos(position(distance)); + + if (isInvalid(pos)) _marker->setVisible(false); + else { + _marker->setVisible(true); + _marker->setPos(pos); + _markerDistance = distance; + } } void PathItem::setMarkerColor(const QColor &color) diff --git a/src/GUI/pathitem.h b/src/GUI/pathitem.h index 24b4f6a8..1b6c5b82 100644 --- a/src/GUI/pathitem.h +++ b/src/GUI/pathitem.h @@ -42,6 +42,7 @@ protected: MarkerItem *_marker; private: + const PathSegment *segment(qreal x) const; QPointF position(qreal distance) const; void updatePainterPath(); void updateShape(); diff --git a/src/GUI/powergraphitem.cpp b/src/GUI/powergraphitem.cpp index dd6d4d50..13ae8885 100644 --- a/src/GUI/powergraphitem.cpp +++ b/src/GUI/powergraphitem.cpp @@ -6,17 +6,6 @@ PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent) { - qreal sum = 0; - _max = graph.first().y(); - - for (int i = 1; i < graph.size(); i++) { - qreal y = graph.at(i).y(); - sum += y * (graph.at(i).s() - graph.at(i-1).s()); - if (y > _max) - _max = y; - } - _avg = sum/graph.last().s(); - setToolTip(toolTip()); } diff --git a/src/GUI/powergraphitem.h b/src/GUI/powergraphitem.h index fc1be6ce..fe0f6f90 100644 --- a/src/GUI/powergraphitem.h +++ b/src/GUI/powergraphitem.h @@ -11,13 +11,8 @@ public: PowerGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent = 0); - qreal max() const {return _max;} - qreal avg() const {return _avg;} - private: QString toolTip() const; - - qreal _avg, _max; }; #endif // POWERGRAPHITEM_H diff --git a/src/GUI/routeitem.cpp b/src/GUI/routeitem.cpp index e7069613..5be3814c 100644 --- a/src/GUI/routeitem.cpp +++ b/src/GUI/routeitem.cpp @@ -15,7 +15,8 @@ QString RouteItem::toolTip(Units units) const tt.insert(tr("Name"), _name); if (!_desc.isEmpty()) tt.insert(tr("Description"), _desc); - tt.insert(tr("Distance"), Format::distance(_path.last().distance(), units)); + tt.insert(tr("Distance"), Format::distance(_path.last().last().distance(), + units)); return tt.toString(); } diff --git a/src/GUI/speedgraphitem.cpp b/src/GUI/speedgraphitem.cpp index 03956847..1ba775c3 100644 --- a/src/GUI/speedgraphitem.cpp +++ b/src/GUI/speedgraphitem.cpp @@ -10,15 +10,9 @@ SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, _units = Metric; _timeType = Total; - _avg = graph.last().s() / graph.last().t(); - _mavg = graph.last().s() / movingTime; - - _max = graph.first().y(); - for (int i = 1; i < graph.size(); i++) { - qreal y = graph.at(i).y(); - if (y > _max) - _max = y; - } + _max = GraphItem::max(); + _avg = graph.last().last().s() / graph.last().last().t(); + _mavg = graph.last().last().s() / movingTime; setToolTip(toolTip()); } diff --git a/src/GUI/speedgraphitem.h b/src/GUI/speedgraphitem.h index 3ace562f..af650ca0 100644 --- a/src/GUI/speedgraphitem.h +++ b/src/GUI/speedgraphitem.h @@ -12,9 +12,9 @@ public: SpeedGraphItem(const Graph &graph, GraphType type, qreal movingTime, QGraphicsItem *parent = 0); - qreal max() const {return _max;} qreal avg() const {return _avg;} qreal mavg() const {return _mavg;} + qreal max() const {return _max;} void setUnits(Units units); void setTimeType(TimeType type); diff --git a/src/GUI/temperaturegraphitem.cpp b/src/GUI/temperaturegraphitem.cpp index c43df7c9..42a79620 100644 --- a/src/GUI/temperaturegraphitem.cpp +++ b/src/GUI/temperaturegraphitem.cpp @@ -6,20 +6,9 @@ TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type, QGraphicsItem *parent) : GraphItem(graph, type, parent) { - qreal sum = 0; - _min = _max = graph.first().y(); - - for (int j = 1; j < graph.size(); j++) { - qreal y = graph.at(j).y(); - - sum += graph.at(j).y() * (graph.at(j).s() - graph.at(j-1).s()); - - if (y > _max) - _max = y; - if (y < _min) - _min = y; - } - _avg = sum/graph.last().s(); + _min = GraphItem::min(); + _max = GraphItem::max(); + _avg = GraphItem::avg(); setToolTip(toolTip(Metric)); } diff --git a/src/GUI/temperaturegraphitem.h b/src/GUI/temperaturegraphitem.h index 2b475b4d..903eab6b 100644 --- a/src/GUI/temperaturegraphitem.h +++ b/src/GUI/temperaturegraphitem.h @@ -20,7 +20,7 @@ public: private: QString toolTip(Units units) const; - qreal _avg, _min, _max; + qreal _min, _max, _avg; }; #endif // TEMPERATUREGRAPHITEM_H diff --git a/src/GUI/trackitem.cpp b/src/GUI/trackitem.cpp index 7fa8dacc..bda072d6 100644 --- a/src/GUI/trackitem.cpp +++ b/src/GUI/trackitem.cpp @@ -13,7 +13,8 @@ QString TrackItem::toolTip(Units units) const tt.insert(tr("Name"), _name); if (!_desc.isEmpty()) tt.insert(tr("Description"), _desc); - tt.insert(tr("Distance"), Format::distance(_path.last().distance(), units)); + tt.insert(tr("Distance"), Format::distance(_path.last().last().distance(), + units)); if (_time > 0) tt.insert(tr("Total time"), Format::timeSpan(_time)); if (_movingTime > 0) diff --git a/src/data/fitparser.cpp b/src/data/fitparser.cpp index 02e073b0..3dd4f0cb 100644 --- a/src/data/fitparser.cpp +++ b/src/data/fitparser.cpp @@ -58,7 +58,7 @@ public: MessageDefinition defs[16]; qreal ratio; Trackpoint trackpoint; - TrackData track; + SegmentData segment; }; @@ -306,7 +306,7 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def) ctx.trackpoint.setTimestamp(QDateTime::fromTime_t(ctx.timestamp + 631065600)); ctx.trackpoint.setRatio(ctx.ratio); - ctx.track.append(ctx.trackpoint); + ctx.segment.append(ctx.trackpoint); ctx.trackpoint = Trackpoint(); ctx.lastWrite = ctx.timestamp; } @@ -388,7 +388,8 @@ bool FITParser::parse(QFile *file, QList &tracks, if (!parseRecord(ctx)) return false; - tracks.append(ctx.track); + tracks.append(TrackData()); + tracks.last().append(ctx.segment); return true; } diff --git a/src/data/geojsonparser.cpp b/src/data/geojsonparser.cpp index cce47e5e..c0086831 100644 --- a/src/data/geojsonparser.cpp +++ b/src/data/geojsonparser.cpp @@ -70,17 +70,9 @@ bool GeoJSONParser::multiPoint(const QJsonArray &coordinates, return true; } -bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, - const QJsonObject &properties) +bool GeoJSONParser::lineString(const QJsonArray &coordinates, + SegmentData &segment) { - if (properties.contains("title") && properties["title"].isString()) - track.setName(properties["title"].toString()); - if (properties.contains("name") && properties["name"].isString()) - track.setName(properties["name"].toString()); - if (properties.contains("description") - && properties["description"].isString()) - track.setDescription(properties["description"].toString()); - for (int i = 0; i < coordinates.size(); i++) { QJsonArray point(coordinates.at(i).toArray()); if (point.count() < 2 || !point.at(0).isDouble() @@ -93,23 +85,48 @@ bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, point.at(1).toDouble())); if (point.count() == 3 && point.at(2).isDouble()) t.setElevation(point.at(2).toDouble()); - track.append(t); + segment.append(t); } return true; } -bool GeoJSONParser::multiLineString(const QJsonArray &coordinates, - QList &tracks, const QJsonObject &properties) +bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, + const QJsonObject &properties) { + if (properties.contains("title") && properties["title"].isString()) + track.setName(properties["title"].toString()); + if (properties.contains("name") && properties["name"].isString()) + track.setName(properties["name"].toString()); + if (properties.contains("description") + && properties["description"].isString()) + track.setDescription(properties["description"].toString()); + + track.append(SegmentData()); + + lineString(coordinates, track.last()); + + return true; +} + +bool GeoJSONParser::multiLineString(const QJsonArray &coordinates, + TrackData &track, const QJsonObject &properties) +{ + if (properties.contains("title") && properties["title"].isString()) + track.setName(properties["title"].toString()); + if (properties.contains("name") && properties["name"].isString()) + track.setName(properties["name"].toString()); + if (properties.contains("description") + && properties["description"].isString()) + track.setDescription(properties["description"].toString()); + for (int i = 0; i < coordinates.size(); i++) { if (!coordinates.at(i).isArray()) { _errorString = "Invalid MultiLineString coordinates"; return false; } else { - tracks.append(TrackData()); - if (!lineString(coordinates.at(i).toArray(), tracks.last(), - properties)) + track.append(SegmentData()); + if (!lineString(coordinates.at(i).toArray(), track.last())) return false; } } @@ -215,8 +232,9 @@ bool GeoJSONParser::geometryCollection(const QJsonObject &json, return false; break; case MultiLineString: - if (!multiLineString(geometry["coordinates"].toArray(), tracks, - properties)) + tracks.append(TrackData()); + if (!multiLineString(geometry["coordinates"].toArray(), + tracks.last(), properties)) return false; break; case Polygon: @@ -265,8 +283,9 @@ bool GeoJSONParser::feature(const QJsonObject &json, QList &tracks, return lineString(geometry["coordinates"].toArray(), tracks.last(), properties); case MultiLineString: - return multiLineString(geometry["coordinates"].toArray(), tracks, - properties); + tracks.append(TrackData()); + return multiLineString(geometry["coordinates"].toArray(), + tracks.last(), properties); case GeometryCollection: return geometryCollection(geometry, tracks, areas, waypoints); case Polygon: @@ -327,7 +346,8 @@ bool GeoJSONParser::parse(QFile *file, QList &tracks, tracks.append(TrackData()); return lineString(json["coordinates"].toArray(), tracks.last()); case MultiLineString: - return multiLineString(json["coordinates"].toArray(), tracks); + tracks.append(TrackData()); + return multiLineString(json["coordinates"].toArray(), tracks.last()); case GeometryCollection: return geometryCollection(json, tracks, areas, waypoints); case Feature: diff --git a/src/data/geojsonparser.h b/src/data/geojsonparser.h index 7a711123..7cbd13e4 100644 --- a/src/data/geojsonparser.h +++ b/src/data/geojsonparser.h @@ -34,10 +34,11 @@ private: const QJsonObject &properties = QJsonObject()); bool multiPoint(const QJsonArray &coordinates, QVector &waypoints, const QJsonObject &properties = QJsonObject()); + bool lineString(const QJsonArray &coordinates, SegmentData &segment); bool lineString(const QJsonArray &coordinates, TrackData &track, const QJsonObject &properties = QJsonObject()); bool multiLineString(const QJsonArray &coordinates, - QList &tracks, const QJsonObject &properties = QJsonObject()); + TrackData &track, const QJsonObject &properties = QJsonObject()); bool polygon(const QJsonArray &coordinates, ::Polygon &pg); bool polygon(const QJsonArray &coordinates, Area &area, const QJsonObject &properties = QJsonObject()); diff --git a/src/data/gpxparser.cpp b/src/data/gpxparser.cpp index 0286c086..5c6a6324 100644 --- a/src/data/gpxparser.cpp +++ b/src/data/gpxparser.cpp @@ -51,7 +51,7 @@ Coordinates GPXParser::coordinates() return Coordinates(lon, lat); } -void GPXParser::rpExtension(TrackData *autoRoute) +void GPXParser::rpExtension(SegmentData *autoRoute) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("rpt")) @@ -72,7 +72,7 @@ void GPXParser::tpExtension(Trackpoint &trackpoint) } } -void GPXParser::rteptExtensions(TrackData *autoRoute) +void GPXParser::rteptExtensions(SegmentData *autoRoute) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("RoutePointExtension")) @@ -124,7 +124,7 @@ void GPXParser::trackpointData(Trackpoint &trackpoint) trackpoint.setElevation(trackpoint.elevation() - gh); } -void GPXParser::waypointData(Waypoint &waypoint, TrackData *autoRoute) +void GPXParser::waypointData(Waypoint &waypoint, SegmentData *autoRoute) { qreal gh = NAN; @@ -149,12 +149,12 @@ void GPXParser::waypointData(Waypoint &waypoint, TrackData *autoRoute) waypoint.setElevation(waypoint.elevation() - gh); } -void GPXParser::trackpoints(TrackData &track) +void GPXParser::trackpoints(SegmentData &segment) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("trkpt")) { - track.append(Trackpoint(coordinates())); - trackpointData(track.last()); + segment.append(Trackpoint(coordinates())); + trackpointData(segment.last()); } else _reader.skipCurrentElement(); } @@ -163,11 +163,13 @@ void GPXParser::trackpoints(TrackData &track) void GPXParser::routepoints(RouteData &route, QList &tracks) { TrackData autoRoute; + autoRoute.append(SegmentData()); + SegmentData &autoRouteSegment = autoRoute.last(); while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("rtept")) { route.append(Waypoint(coordinates())); - waypointData(route.last(), &autoRoute); + waypointData(route.last(), &autoRouteSegment); } else if (_reader.name() == QLatin1String("name")) route.setName(_reader.readElementText()); else if (_reader.name() == QLatin1String("desc")) @@ -176,7 +178,7 @@ void GPXParser::routepoints(RouteData &route, QList &tracks) _reader.skipCurrentElement(); } - if (!autoRoute.isEmpty()) { + if (!autoRouteSegment.isEmpty()) { autoRoute.setName(route.name()); autoRoute.setDescription(route.description()); tracks.append(autoRoute); @@ -186,9 +188,10 @@ void GPXParser::routepoints(RouteData &route, QList &tracks) void GPXParser::track(TrackData &track) { while (_reader.readNextStartElement()) { - if (_reader.name() == QLatin1String("trkseg")) - trackpoints(track); - else if (_reader.name() == QLatin1String("name")) + if (_reader.name() == QLatin1String("trkseg")) { + track.append(SegmentData()); + trackpoints(track.last()); + } else if (_reader.name() == QLatin1String("name")) track.setName(_reader.readElementText()); else if (_reader.name() == QLatin1String("desc")) track.setDescription(_reader.readElementText()); diff --git a/src/data/gpxparser.h b/src/data/gpxparser.h index 978f83fc..d94cfff6 100644 --- a/src/data/gpxparser.h +++ b/src/data/gpxparser.h @@ -17,16 +17,16 @@ private: void gpx(QList &tracks, QList &routes, QList &areas, QVector &waypoints); void track(TrackData &track); - void trackpoints(TrackData &track); + void trackpoints(SegmentData &segment); void routepoints(RouteData &route, QList &tracks); - void rpExtension(TrackData *autoRoute); + void rpExtension(SegmentData *autoRoute); void tpExtension(Trackpoint &trackpoint); void trkptExtensions(Trackpoint &trackpoint); - void rteptExtensions(TrackData *autoRoute); + void rteptExtensions(SegmentData *autoRoute); void area(Area &area); void gpxExtensions(QList &areas); void trackpointData(Trackpoint &trackpoint); - void waypointData(Waypoint &waypoint, TrackData *autoRoute = 0); + void waypointData(Waypoint &waypoint, SegmentData *autoRoute = 0); qreal number(); QDateTime time(); Coordinates coordinates(); diff --git a/src/data/graph.h b/src/data/graph.h index 68e31614..a2be43e6 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -1,6 +1,7 @@ #ifndef GRAPH_H #define GRAPH_H +#include #include #include #include @@ -39,14 +40,30 @@ inline QDebug operator<<(QDebug dbg, const GraphPoint &point) } #endif // QT_NO_DEBUG -class Graph : public QVector +typedef QVector GraphSegment; + +class Graph : public QList { public: - Graph() {} - Graph(int size) : QVector(size) {} - Graph(const Graph &other) : QVector(other) {} - - bool isValid() const {return size() >= 2;} + bool isValid() const + { + if (isEmpty()) + return false; + for (int i = 0; i < size(); i++) + if (at(i).size() < 2) + return false; + return true; + } + bool hasTime() const + { + for (int i = 0; i < size(); i++) { + const GraphSegment &segment = at(i); + for (int j = 0; j < segment.size(); j++) + if (std::isnan(segment.at(j).t())) + return false; + } + return true; + } }; #endif // GRAPH_H diff --git a/src/data/igcparser.cpp b/src/data/igcparser.cpp index a1ffa926..dbad2ad5 100644 --- a/src/data/igcparser.cpp +++ b/src/data/igcparser.cpp @@ -122,7 +122,8 @@ bool IGCParser::readHRecord(const char *line, int len) return true; } -bool IGCParser::readBRecord(TrackData &track, const char *line, int len) +bool IGCParser::readBRecord(SegmentData &segment, const char *line, + int len) { qreal lat, lon, ele; QTime time; @@ -158,7 +159,7 @@ bool IGCParser::readBRecord(TrackData &track, const char *line, int len) Trackpoint t(Coordinates(lon, lat)); t.setTimestamp(QDateTime(_date, _time, Qt::UTC)); t.setElevation(ele); - track.append(t); + segment.append(t); return true; } @@ -240,10 +241,11 @@ bool IGCParser::parse(QFile *file, QList &tracks, } if (!track) { tracks.append(TrackData()); + tracks.last().append(SegmentData()); _time = QTime(0, 0); track = true; } - if (!readBRecord(tracks.last(), line, len)) + if (!readBRecord(tracks.last().last(), line, len)) return false; } } diff --git a/src/data/igcparser.h b/src/data/igcparser.h index 548b5402..85eee3b5 100644 --- a/src/data/igcparser.h +++ b/src/data/igcparser.h @@ -18,7 +18,7 @@ public: private: bool readHRecord(const char *line, int len); - bool readBRecord(TrackData &track, const char *line, int len); + bool readBRecord(SegmentData &segment, const char *line, int len); bool readCRecord(RouteData &route, const char *line, int len); int _errorLine; diff --git a/src/data/kmlparser.cpp b/src/data/kmlparser.cpp index 8a52ea17..7def0b69 100644 --- a/src/data/kmlparser.cpp +++ b/src/data/kmlparser.cpp @@ -127,7 +127,7 @@ bool KMLParser::pointCoordinates(Waypoint &waypoint) return true; } -bool KMLParser::lineCoordinates(TrackData &track) +bool KMLParser::lineCoordinates(SegmentData &segment) { QString data = _reader.readElementText(); const QChar *sp, *ep, *cp, *vp; @@ -170,11 +170,11 @@ bool KMLParser::lineCoordinates(TrackData &track) if (!res) return false; - track.append(Trackpoint(Coordinates(val[0], val[1]))); - if (!track.last().coordinates().isValid()) + segment.append(Trackpoint(Coordinates(val[0], val[1]))); + if (!segment.last().coordinates().isValid()) return false; if (c == 2) - track.last().setElevation(val[2]); + segment.last().setElevation(val[2]); while (cp->isSpace()) cp++; @@ -257,11 +257,11 @@ QDateTime KMLParser::timeStamp() return ts; } -void KMLParser::lineString(TrackData &track) +void KMLParser::lineString(SegmentData &segment) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("coordinates")) { - if (!lineCoordinates(track)) + if (!lineCoordinates(segment)) _reader.raiseError("Invalid coordinates"); } else _reader.skipCurrentElement(); @@ -328,15 +328,15 @@ void KMLParser::point(Waypoint &waypoint) _reader.raiseError("Missing Point coordinates"); } -void KMLParser::heartRate(TrackData &track, int start) +void KMLParser::heartRate(SegmentData &segment, int start) { int i = start; const char error[] = "Heartrate data count mismatch"; while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("value")) { - if (i < track.size()) - track[i++].setHeartRate(number()); + if (i < segment.size()) + segment[i++].setHeartRate(number()); else { _reader.raiseError(error); return; @@ -345,19 +345,19 @@ void KMLParser::heartRate(TrackData &track, int start) _reader.skipCurrentElement(); } - if (i != track.size()) + if (i != segment.size()) _reader.raiseError(error); } -void KMLParser::cadence(TrackData &track, int start) +void KMLParser::cadence(SegmentData &segment, int start) { int i = start; const char error[] = "Cadence data count mismatch"; while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("value")) { - if (i < track.size()) - track[i++].setCadence(number()); + if (i < segment.size()) + segment[i++].setCadence(number()); else { _reader.raiseError(error); return; @@ -366,19 +366,19 @@ void KMLParser::cadence(TrackData &track, int start) _reader.skipCurrentElement(); } - if (i != track.size()) + if (i != segment.size()) _reader.raiseError(error); } -void KMLParser::speed(TrackData &track, int start) +void KMLParser::speed(SegmentData &segment, int start) { int i = start; const char error[] = "Speed data count mismatch"; while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("value")) { - if (i < track.size()) - track[i++].setSpeed(number()); + if (i < segment.size()) + segment[i++].setSpeed(number()); else { _reader.raiseError(error); return; @@ -387,19 +387,19 @@ void KMLParser::speed(TrackData &track, int start) _reader.skipCurrentElement(); } - if (i != track.size()) + if (i != segment.size()) _reader.raiseError(error); } -void KMLParser::temperature(TrackData &track, int start) +void KMLParser::temperature(SegmentData &segment, int start) { int i = start; const char error[] = "Temperature data count mismatch"; while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("value")) { - if (i < track.size()) - track[i++].setTemperature(number()); + if (i < segment.size()) + segment[i++].setTemperature(number()); else { _reader.raiseError(error); return; @@ -408,11 +408,11 @@ void KMLParser::temperature(TrackData &track, int start) _reader.skipCurrentElement(); } - if (i != track.size()) + if (i != segment.size()) _reader.raiseError(error); } -void KMLParser::schemaData(TrackData &track, int start) +void KMLParser::schemaData(SegmentData &segment, int start) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("SimpleArrayData")) { @@ -420,13 +420,13 @@ void KMLParser::schemaData(TrackData &track, int start) QStringRef name = attr.value("name"); if (name == QLatin1String("Heartrate")) - heartRate(track, start); + heartRate(segment, start); else if (name == QLatin1String("Cadence")) - cadence(track, start); + cadence(segment, start); else if (name == QLatin1String("Speed")) - speed(track, start); + speed(segment, start); else if (name == QLatin1String("Temperature")) - temperature(track, start); + temperature(segment, start); else _reader.skipCurrentElement(); } else @@ -434,51 +434,52 @@ void KMLParser::schemaData(TrackData &track, int start) } } -void KMLParser::extendedData(TrackData &track, int start) +void KMLParser::extendedData(SegmentData &segment, int start) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("SchemaData")) - schemaData(track, start); + schemaData(segment, start); else _reader.skipCurrentElement(); } } -void KMLParser::track(TrackData &track) +void KMLParser::track(SegmentData &segment) { const char error[] = "gx:coord/when element count mismatch"; - int first = track.size(); + int first = segment.size(); int i = first; while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("when")) { - track.append(Trackpoint()); - track.last().setTimestamp(time()); + segment.append(Trackpoint()); + segment.last().setTimestamp(time()); } else if (_reader.name() == QLatin1String("coord")) { - if (i == track.size()) { + if (i == segment.size()) { _reader.raiseError(error); return; - } else if (!coord(track[i])) { + } else if (!coord(segment[i])) { _reader.raiseError("Invalid coordinates"); return; } i++; } else if (_reader.name() == QLatin1String("ExtendedData")) - extendedData(track, first); + extendedData(segment, first); else _reader.skipCurrentElement(); } - if (i != track.size()) + if (i != segment.size()) _reader.raiseError(error); } void KMLParser::multiTrack(TrackData &t) { while (_reader.readNextStartElement()) { - if (_reader.name() == QLatin1String("Track")) - track(t); - else + if (_reader.name() == QLatin1String("Track")) { + t.append(SegmentData()); + track(t.last()); + } else _reader.skipCurrentElement(); } } @@ -498,9 +499,10 @@ void KMLParser::multiGeometry(QList &tracks, QList &areas, } else if (_reader.name() == QLatin1String("LineString")) { tracks.append(TrackData()); TrackData &t = tracks.last(); + t.append(SegmentData()); t.setName(name); t.setDescription(desc); - lineString(t); + lineString(t.last()); } else if (_reader.name() == QLatin1String("Polygon")) { areas.append(Area()); Area &a = areas.last(); @@ -538,15 +540,17 @@ void KMLParser::placemark(QList &tracks, QList &areas, || _reader.name() == QLatin1String("LinearRing")) { tracks.append(TrackData()); TrackData &t = tracks.last(); + t.append(SegmentData()); t.setName(name); t.setDescription(desc); - lineString(t); + lineString(t.last()); } else if (_reader.name() == QLatin1String("Track")) { tracks.append(TrackData()); TrackData &t = tracks.last(); + t.append(SegmentData()); t.setName(name); t.setDescription(desc); - track(t); + track(t.last()); } else if (_reader.name() == QLatin1String("MultiTrack")) { tracks.append(TrackData()); TrackData &t = tracks.last(); diff --git a/src/data/kmlparser.h b/src/data/kmlparser.h index d497c379..6b9ee039 100644 --- a/src/data/kmlparser.h +++ b/src/data/kmlparser.h @@ -25,23 +25,23 @@ private: void multiGeometry(QList &tracks, QList &areas, QVector &waypoints, const QString &name, const QString &desc, const QDateTime timestamp); - void track(TrackData &track); + void track(SegmentData &segment); void multiTrack(TrackData &t); - void lineString(TrackData &track); + void lineString(SegmentData &segment); void linearRing(QVector &coordinates); void boundary(QVector &coordinates); void polygon(Area &area); void point(Waypoint &waypoint); bool pointCoordinates(Waypoint &waypoint); - bool lineCoordinates(TrackData &track); + bool lineCoordinates(SegmentData &segment); bool polygonCoordinates(QVector &points); bool coord(Trackpoint &trackpoint); - void extendedData(TrackData &track, int start); - void schemaData(TrackData &track, int start); - void heartRate(TrackData &track, int start); - void cadence(TrackData &track, int start); - void speed(TrackData &track, int start); - void temperature(TrackData &track, int start); + void extendedData(SegmentData &segment, int start); + void schemaData(SegmentData &segment, int start); + void heartRate(SegmentData &segment, int start); + void cadence(SegmentData &segment, int start); + void speed(SegmentData &segment, int start); + void temperature(SegmentData &segment, int start); QDateTime timeStamp(); qreal number(); QDateTime time(); diff --git a/src/data/nmeaparser.cpp b/src/data/nmeaparser.cpp index 878959a1..d3e69787 100644 --- a/src/data/nmeaparser.cpp +++ b/src/data/nmeaparser.cpp @@ -227,7 +227,7 @@ bool NMEAParser::readEW(const char *data, int len, qreal &lon) return true; } -bool NMEAParser::readRMC(TrackData &track, const char *line, int len) +bool NMEAParser::readRMC(SegmentData &segment, const char *line, int len) { int col = 1; const char *vp = line; @@ -280,8 +280,8 @@ bool NMEAParser::readRMC(TrackData &track, const char *line, int len) } if (!date.isNull()) { - if (_date.isNull() && !_time.isNull() && !track.isEmpty()) - track.last().setTimestamp(QDateTime(date, _time, Qt::UTC)); + if (_date.isNull() && !_time.isNull() && !segment.isEmpty()) + segment.last().setTimestamp(QDateTime(date, _time, Qt::UTC)); _date = date; } @@ -290,13 +290,13 @@ bool NMEAParser::readRMC(TrackData &track, const char *line, int len) Trackpoint t(c); if (!_date.isNull() && !time.isNull()) t.setTimestamp(QDateTime(_date, time, Qt::UTC)); - track.append(t); + segment.append(t); } return true; } -bool NMEAParser::readGGA(TrackData &track, const char *line, int len) +bool NMEAParser::readGGA(SegmentData &segment, const char *line, int len) { int col = 1; const char *vp = line; @@ -364,7 +364,7 @@ bool NMEAParser::readGGA(TrackData &track, const char *line, int len) t.setTimestamp(QDateTime(_date, _time, Qt::UTC)); if (!std::isnan(ele)) t.setElevation(ele - gh); - track.append(t); + segment.append(t); _GGA = true; } @@ -485,6 +485,7 @@ bool NMEAParser::parse(QFile *file, QList &tracks, Q_UNUSED(polygons); qint64 len; char line[80 + 2 + 1 + 1]; + SegmentData segment; _errorLine = 1; @@ -493,9 +494,6 @@ bool NMEAParser::parse(QFile *file, QList &tracks, _time = QTime(); _GGA = false; - tracks.append(TrackData()); - TrackData &track = tracks.last(); - while (!file->atEnd()) { len = file->readLine(line, sizeof(line)); @@ -509,10 +507,10 @@ bool NMEAParser::parse(QFile *file, QList &tracks, if (validSentence(line, len)) { if (!memcmp(line + 3, "RMC,", 4)) { - if (!readRMC(track, line + 7, len - 7)) + if (!readRMC(segment, line + 7, len - 7)) return false; } else if (!memcmp(line + 3, "GGA,", 4)) { - if (!readGGA(track, line + 7, len - 7)) + if (!readGGA(segment, line + 7, len - 7)) return false; } else if (!memcmp(line + 3, "WPL,", 4)) { if (!readWPL(waypoints, line + 7, len - 7)) @@ -526,10 +524,15 @@ bool NMEAParser::parse(QFile *file, QList &tracks, _errorLine++; } - if (!tracks.last().size() && !waypoints.size()) { + if (!segment.size() && !waypoints.size()) { _errorString = "No usable NMEA sentence found"; return false; } + if (segment.size()) { + tracks.append(TrackData()); + tracks.last().append(segment); + } + return true; } diff --git a/src/data/nmeaparser.h b/src/data/nmeaparser.h index f06e72b7..f34cf860 100644 --- a/src/data/nmeaparser.h +++ b/src/data/nmeaparser.h @@ -25,8 +25,8 @@ private: bool readAltitude(const char *data, int len, qreal &ele); bool readGeoidHeight(const char *data, int len, qreal &gh); - bool readRMC(TrackData &track, const char *line, int len); - bool readGGA(TrackData &track, const char *line, int len); + bool readRMC(SegmentData &segment, const char *line, int len); + bool readGGA(SegmentData &segment, const char *line, int len); bool readWPL(QVector &waypoints, const char *line, int len); bool readZDA(const char *line, int len); diff --git a/src/data/oziparsers.cpp b/src/data/oziparsers.cpp index caf77f5f..47163ff2 100644 --- a/src/data/oziparsers.cpp +++ b/src/data/oziparsers.cpp @@ -41,6 +41,8 @@ bool PLTParser::parse(QFile *file, QList &tracks, tracks.append(TrackData()); TrackData &track = tracks.last(); + track.append(SegmentData()); + SegmentData &segment = track.last(); while (!file->atEnd()) { QByteArray line = file->readLine(); @@ -105,7 +107,8 @@ bool PLTParser::parse(QFile *file, QList &tracks, } } - track.append(tp); + + segment.append(tp); } _errorLine++; diff --git a/src/data/path.cpp b/src/data/path.cpp index d449710c..9b9c78ac 100644 --- a/src/data/path.cpp +++ b/src/data/path.cpp @@ -1,14 +1,27 @@ #include "path.h" +bool Path::isValid() const +{ + if (isEmpty()) + return false; + for (int i = 0; i < size(); i++) + if (at(i).size() < 2) + return false; + return true; +} + RectC Path::boundingRect() const { RectC ret; - if (size() < 2) + if (!isValid()) return ret; - for (int i = 0; i < size(); i++) - ret = ret.united(at(i).coordinates()); + for (int i = 0; i < size(); i++) { + const PathSegment &segment = at(i); + for (int j = 0; j < segment.size(); j++) + ret = ret.united(segment.at(j).coordinates()); + } return ret; } diff --git a/src/data/path.h b/src/data/path.h index 461a97b9..74cfa572 100644 --- a/src/data/path.h +++ b/src/data/path.h @@ -27,10 +27,12 @@ Q_DECLARE_TYPEINFO(PathPoint, Q_PRIMITIVE_TYPE); QDebug operator<<(QDebug dbg, const PathPoint &point); #endif // QT_NO_DEBUG +typedef QVector PathSegment; -class Path : public QVector +class Path : public QList { public: + bool isValid() const; RectC boundingRect() const; }; diff --git a/src/data/poi.cpp b/src/data/poi.cpp index 42041273..22e8ccb6 100644 --- a/src/data/poi.cpp +++ b/src/data/poi.cpp @@ -129,23 +129,28 @@ QList POI::points(const Path &path) const QSet::const_iterator it; - for (int i = 1; i < path.count(); i++) { - double ds = path.at(i).distance() - path.at(i-1).distance(); - unsigned n = (unsigned)ceil(ds / _radius); + for (int i = 0; i < path.count(); i++) { + const PathSegment &segment = path.at(i); - if (n > 1) { - GreatCircle gc(path.at(i-1).coordinates(), path.at(i).coordinates()); - for (unsigned j = 0; j < n; j++) { - RectC br(gc.pointAt((double)j/n), _radius); + for (int j = 1; j < segment.size(); j++) { + double ds = segment.at(j).distance() - segment.at(j-1).distance(); + unsigned n = (unsigned)ceil(ds / _radius); + + if (n > 1) { + GreatCircle gc(segment.at(j-1).coordinates(), + segment.at(j).coordinates()); + for (unsigned k = 0; k < n; k++) { + RectC br(gc.pointAt((double)k/n), _radius); + search(br, set); + } + } else { + RectC br(segment.at(j-1).coordinates(), _radius); search(br, set); } - } else { - RectC br(path.at(i-1).coordinates(), _radius); - search(br, set); } } - RectC br(path.last().coordinates(), _radius); + RectC br(path.last().last().coordinates(), _radius); search(br, set); diff --git a/src/data/route.cpp b/src/data/route.cpp index 804395d6..378906ee 100644 --- a/src/data/route.cpp +++ b/src/data/route.cpp @@ -19,9 +19,11 @@ Route::Route(const RouteData &data) : _data(data) Path Route::path() const { Path ret; + ret.append(PathSegment()); + PathSegment &ps = ret.last(); for (int i = 0; i < _data.size(); i++) - ret.append(PathPoint(_data.at(i).coordinates(), _distance.at(i))); + ps.append(PathPoint(_data.at(i).coordinates(), _distance.at(i))); return ret; } @@ -29,17 +31,19 @@ Path Route::path() const Graph Route::elevation() const { Graph graph; + graph.append(GraphSegment()); + GraphSegment &gs = graph.last(); for (int i = 0; i < _data.size(); i++) { if (_data.at(i).hasElevation() && !_useDEM) - graph.append(GraphPoint(_distance.at(i), NAN, + gs.append(GraphPoint(_distance.at(i), NAN, _data.at(i).elevation())); else { qreal elevation = DEM::elevation(_data.at(i).coordinates()); if (!std::isnan(elevation)) - graph.append(GraphPoint(_distance.at(i), NAN, elevation)); + gs.append(GraphPoint(_distance.at(i), NAN, elevation)); else if (_data.at(i).hasElevation()) - graph.append(GraphPoint(_distance.at(i), NAN, + gs.append(GraphPoint(_distance.at(i), NAN, _data.at(i).elevation())); } } diff --git a/src/data/slfparser.cpp b/src/data/slfparser.cpp index 3224fcdc..8684b370 100644 --- a/src/data/slfparser.cpp +++ b/src/data/slfparser.cpp @@ -26,7 +26,7 @@ bool SLFParser::data(const QXmlStreamAttributes &attr, const char *name, return res; } -void SLFParser::entries(const QDateTime &date, TrackData &track) +void SLFParser::entries(const QDateTime &date, SegmentData &segment) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Entry")) { @@ -60,7 +60,7 @@ void SLFParser::entries(const QDateTime &date, TrackData &track) if (data(attr, "trainingTimeAbsolute", val)) t.setTimestamp(date.addMSecs(val * 10)); - track.append(t); + segment.append(t); } _reader.skipCurrentElement(); @@ -89,9 +89,10 @@ void SLFParser::activity(TrackData &track) QDateTime date; while (_reader.readNextStartElement()) { - if (_reader.name() == QLatin1String("Entries")) - entries(date, track); - else if (_reader.name() == QLatin1String("GeneralInformation")) + if (_reader.name() == QLatin1String("Entries")) { + track.append(SegmentData()); + entries(date, track.last()); + } else if (_reader.name() == QLatin1String("GeneralInformation")) generalInformation(date, track); else _reader.skipCurrentElement(); diff --git a/src/data/slfparser.h b/src/data/slfparser.h index 02fdb938..c8230b82 100644 --- a/src/data/slfparser.h +++ b/src/data/slfparser.h @@ -16,7 +16,7 @@ class SLFParser : public Parser private: void generalInformation(QDateTime &date, TrackData &track); void activity(TrackData &track); - void entries(const QDateTime &date, TrackData &track); + void entries(const QDateTime &date, SegmentData &segment); bool data(const QXmlStreamAttributes &attr, const char *name, qreal &val); void warning(const char *text) const; diff --git a/src/data/tcxparser.cpp b/src/data/tcxparser.cpp index c8ecf6b8..3eae25e4 100644 --- a/src/data/tcxparser.cpp +++ b/src/data/tcxparser.cpp @@ -116,14 +116,14 @@ void TCXParser::waypointData(Waypoint &waypoint) } } -void TCXParser::trackpoints(TrackData &track) +void TCXParser::trackpoints(SegmentData &segment) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Trackpoint")) { Trackpoint t; trackpointData(t); if (t.coordinates().isValid()) - track.append(t); + segment.append(t); else warning("Missing Trackpoint coordinates"); } else @@ -131,11 +131,11 @@ void TCXParser::trackpoints(TrackData &track) } } -void TCXParser::lap(TrackData &track) +void TCXParser::lap(SegmentData &segment) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Track")) - trackpoints(track); + trackpoints(segment); else _reader.skipCurrentElement(); } @@ -144,9 +144,10 @@ void TCXParser::lap(TrackData &track) void TCXParser::course(QVector &waypoints, TrackData &track) { while (_reader.readNextStartElement()) { - if (_reader.name() == QLatin1String("Track")) - trackpoints(track); - else if (_reader.name() == QLatin1String("Name")) + if (_reader.name() == QLatin1String("Track")) { + track.append(SegmentData()); + trackpoints(track.last()); + } else if (_reader.name() == QLatin1String("Name")) track.setName(_reader.readElementText()); else if (_reader.name() == QLatin1String("Notes")) track.setDescription(_reader.readElementText()); @@ -164,9 +165,11 @@ void TCXParser::course(QVector &waypoints, TrackData &track) void TCXParser::activity(TrackData &track) { + track.append(SegmentData()); + while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Lap")) - lap(track); + lap(track.last()); else if (_reader.name() == QLatin1String("Notes")) track.setDescription(_reader.readElementText()); else @@ -190,7 +193,7 @@ void TCXParser::sport(QList &tracks) while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Activity")) { tracks.append(TrackData()); - activity(tracks.back()); + activity(tracks.last()); } else _reader.skipCurrentElement(); } @@ -212,7 +215,7 @@ void TCXParser::activities(QList &tracks) while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Activity")) { tracks.append(TrackData()); - activity(tracks.back()); + activity(tracks.last()); } else if (_reader.name() == QLatin1String("MultiSportSession")) multiSportSession(tracks); else diff --git a/src/data/tcxparser.h b/src/data/tcxparser.h index 21a9f271..1ba692a6 100644 --- a/src/data/tcxparser.h +++ b/src/data/tcxparser.h @@ -21,8 +21,8 @@ private: void sport(QList &tracks); void course(QVector &waypoints, TrackData &track); void activity(TrackData &track); - void lap(TrackData &track); - void trackpoints(TrackData &track); + void lap(SegmentData &segment); + void trackpoints(SegmentData &segment); void trackpointData(Trackpoint &trackpoint); void waypointData(Waypoint &waypoint); void extensions(Trackpoint &trackpoint); diff --git a/src/data/track.cpp b/src/data/track.cpp index 9f22ca5c..837faccf 100644 --- a/src/data/track.cpp +++ b/src/data/track.cpp @@ -45,13 +45,13 @@ static QSet eliminate(const QVector &v) return rm; } -static Graph filter(const Graph &g, int window) +static GraphSegment filter(const GraphSegment &g, int window) { if (g.size() < window || window < 2) - return Graph(g); + return GraphSegment(g); qreal acc = 0; - Graph ret(g.size()); + GraphSegment ret(g.size()); for (int i = 0; i < window; i++) acc += g.at(i).y(); @@ -70,239 +70,319 @@ static Graph filter(const Graph &g, int window) } -Track::Track(const TrackData &data) : _data(data) +Track::Track(const TrackData &data) : _data(data), _pause(0) { - QVector acceleration; qreal ds, dt; - _time.append(0); - _distance.append(0); - _speed.append(0); - acceleration.append(0); - - for (int i = 1; i < _data.count(); i++) { - ds = _data.at(i).coordinates().distanceTo(_data.at(i-1).coordinates()); - _distance.append(_distance.at(i-1) + ds); - - if (_data.first().hasTimestamp() && _data.at(i).hasTimestamp() - && _data.at(i).timestamp() >= _data.at(i-1).timestamp()) - _time.append(_data.first().timestamp().msecsTo( - _data.at(i).timestamp()) / 1000.0); - else - _time.append(NAN); - - dt = _time.at(i) - _time.at(i-1); - if (dt < 1e-3) { - _speed.append(_speed.at(i-1)); - acceleration.append(acceleration.at(i-1)); - } else { - _speed.append(ds / dt); - qreal dv = _speed.at(i) - _speed.at(i-1); - acceleration.append(dv / dt); - } - } - - _pause = 0; - for (int i = 1; i < _data.count(); i++) { - if (_time.at(i) > _time.at(i-1) + _pauseInterval - && _speed.at(i) < _pauseSpeed) { - _pause += _time.at(i) - _time.at(i-1); - _stop.insert(i-1); - _stop.insert(i); - } - } - - if (!_outlierEliminate) - return; - - _outliers = eliminate(acceleration); - - QSet::const_iterator it; - for (it = _stop.constBegin(); it != _stop.constEnd(); ++it) - _outliers.remove(*it); - - int last = 0; for (int i = 0; i < _data.size(); i++) { - if (_outliers.contains(i)) - last++; - else - break; - } - for (int i = last + 1; i < _data.size(); i++) { - if (_outliers.contains(i)) + const SegmentData &sd = _data.at(i); + if (sd.isEmpty()) continue; - if (discardStopPoint(i)) { - _distance[i] = _distance.at(last); - _speed[i] = 0; - } else { - ds = _data.at(i).coordinates().distanceTo( - _data.at(last).coordinates()); - _distance[i] = _distance.at(last) + ds; - dt = _time.at(i) - _time.at(last); - _speed[i] = (dt < 1e-3) ? _speed.at(last) : ds / dt; + // precompute distances, times, speeds and acceleration + QVector acceleration; + + _segments.append(Segment()); + Segment &seg = _segments.last(); + + seg.distance.append(i ? _segments.at(i-1).distance.last() : 0); + seg.time.append(i ? _segments.at(i-1).time.last() : + sd.first().hasTimestamp() ? 0 : NAN); + seg.speed.append(sd.first().hasTimestamp() ? 0 : NAN); + acceleration.append(sd.first().hasTimestamp() ? 0 : NAN); + + for (int j = 1; j < sd.size(); j++) { + ds = sd.at(j).coordinates().distanceTo( + sd.at(j-1).coordinates()); + seg.distance.append(seg.distance.last() + ds); + + if (sd.at(j).timestamp() >= sd.at(j-1).timestamp()) + dt = sd.at(j-1).timestamp().msecsTo( + sd.at(j).timestamp()) / 1000.0; + else + dt = NAN; + seg.time.append(seg.time.last() + dt); + + if (dt < 1e-3) { + seg.speed.append(seg.speed.last()); + acceleration.append(acceleration.last()); + } else { + qreal v = ds / dt; + qreal dv = v - seg.speed.last(); + seg.speed.append(v); + acceleration.append(dv / dt); + } + } + + // get stop-points + pause duration + for (int j = 1; j < seg.time.size(); j++) { + if (seg.time.at(j) > seg.time.at(j-1) + _pauseInterval + && seg.speed.at(j) < _pauseSpeed) { + _pause += seg.time.at(j) - seg.time.at(j-1); + seg.stop.insert(j-1); + seg.stop.insert(j); + } + } + + if (!_outlierEliminate) + continue; + + + // eliminate outliers + seg.outliers = eliminate(acceleration); + + // stop-points can not be outliers + QSet::const_iterator it; + for (it = seg.stop.constBegin(); it != seg.stop.constEnd(); ++it) + seg.outliers.remove(*it); + + // recompute distances (and dependand data) without outliers + int last = 0; + for (int j = 0; j < sd.size(); j++) { + if (seg.outliers.contains(j)) + last++; + else + break; + } + for (int j = last + 1; j < sd.size(); j++) { + if (seg.outliers.contains(i)) + continue; + if (discardStopPoint(seg, j)) { + seg.distance[j] = seg.distance.at(last); + seg.speed[j] = 0; + } else { + ds = sd.at(j).coordinates().distanceTo( + sd.at(last).coordinates()); + seg.distance[j] = seg.distance.at(last) + ds; + + dt = seg.time.at(i) - seg.time.at(last); + seg.speed[i] = (dt < 1e-3) ? seg.speed.at(last) : ds / dt; + } + last = j; } - last = i; } } Graph Track::elevation() const { - Graph raw; + Graph ret; for (int i = 0; i < _data.size(); i++) { - if (_outliers.contains(i)) - continue; + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; - if (_data.at(i).hasElevation() && !_useDEM) - raw.append(GraphPoint(_distance.at(i), _time.at(i), - _data.at(i).elevation())); - else { - qreal elevation = DEM::elevation(_data.at(i).coordinates()); - if (!std::isnan(elevation)) - raw.append(GraphPoint(_distance.at(i), _time.at(i), elevation)); - else if (_data.at(i).hasElevation()) - raw.append(GraphPoint(_distance.at(i), _time.at(i), - _data.at(i).elevation())); + for (int j = 0; j < sd.size(); j++) { + if (seg.outliers.contains(j)) + continue; + + if (sd.at(j).hasElevation() && !_useDEM) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + sd.at(j).elevation())); + else { + qreal elevation = DEM::elevation(sd.at(j).coordinates()); + if (!std::isnan(elevation)) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + elevation)); + else if (sd.at(j).hasElevation()) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + sd.at(j).elevation())); + } } + + ret.append(filter(gs, _elevationWindow)); } - return filter(raw, _elevationWindow); + return ret; } Graph Track::speed() const { - Graph raw, filtered; - qreal v; - QList stop; + Graph ret; for (int i = 0; i < _data.size(); i++) { - if (_stop.contains(i) && (!std::isnan(_speed.at(i)) - || _data.at(i).hasSpeed())) { - v = 0; - stop.append(raw.size()); - } else if (_useReportedSpeed && _data.at(i).hasSpeed() - && !_outliers.contains(i)) - v = _data.at(i).speed(); - else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i)) - v = _speed.at(i); - else - continue; + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; + QList stop; + qreal v; - raw.append(GraphPoint(_distance.at(i), _time.at(i), v)); + for (int j = 0; j < sd.size(); j++) { + if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j)) + || sd.at(j).hasSpeed())) { + v = 0; + stop.append(gs.size()); + } else if (_useReportedSpeed && sd.at(j).hasSpeed() + && seg.outliers.contains(j)) + v = sd.at(j).speed(); + else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j)) + v = seg.speed.at(j); + else + continue; + + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), v)); + } + + ret.append(filter(gs, _speedWindow)); + GraphSegment &filtered = ret.last(); + + for (int j = 0; j < stop.size(); j++) + filtered[stop.at(j)].setY(0); } - filtered = filter(raw, _speedWindow); - - for (int i = 0; i < stop.size(); i++) - filtered[stop.at(i)].setY(0); - - return filtered; + return ret; } Graph Track::heartRate() const { - Graph raw; + Graph ret; - for (int i = 0; i < _data.count(); i++) - if (_data.at(i).hasHeartRate() && !_outliers.contains(i)) - raw.append(GraphPoint(_distance.at(i), _time.at(i), - _data.at(i).heartRate())); + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; - return filter(raw, _heartRateWindow); + for (int j = 0; j < sd.size(); j++) + if (sd.at(j).hasHeartRate() && !seg.outliers.contains(j)) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + sd.at(j).heartRate())); + + ret.append(filter(gs, _heartRateWindow)); + } + + return ret; } Graph Track::temperature() const { - Graph raw; + Graph ret; - for (int i = 0; i < _data.size(); i++) - if (_data.at(i).hasTemperature() && !_outliers.contains(i)) - raw.append(GraphPoint(_distance.at(i), _time.at(i), - _data.at(i).temperature())); + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; - return raw; + for (int j = 0; j < sd.count(); j++) { + if (sd.at(j).hasTemperature() && !seg.outliers.contains(j)) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + sd.at(j).temperature())); + } + + ret.append(gs); + } + + return ret; } Graph Track::ratio() const { - Graph raw; + Graph ret; - for (int i = 0; i < _data.size(); i++) - if (_data.at(i).hasRatio() && !_outliers.contains(i)) - raw.append(GraphPoint(_distance.at(i), _time.at(i), - _data.at(i).ratio())); + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; - return raw; + for (int j = 0; j < sd.size(); j++) + if (sd.at(j).hasRatio() && !seg.outliers.contains(j)) + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), + sd.at(j).ratio())); + + ret.append(gs); + } + + return ret; } Graph Track::cadence() const { - Graph raw, filtered; - QList stop; - qreal c; + Graph ret; for (int i = 0; i < _data.size(); i++) { - if (_data.at(i).hasCadence() && _stop.contains(i)) { - c = 0; - stop.append(raw.size()); - } else if (_data.at(i).hasCadence() && !_outliers.contains(i)) - c = _data.at(i).cadence(); - else - continue; + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; + QList stop; + qreal c; - raw.append(GraphPoint(_distance.at(i), _time.at(i), c)); + for (int j = 0; j < sd.size(); j++) { + if (sd.at(j).hasCadence() && seg.stop.contains(j)) { + c = 0; + stop.append(gs.size()); + } else if (sd.at(j).hasCadence() && !seg.outliers.contains(j)) + c = sd.at(j).cadence(); + else + continue; + + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), c)); + } + + ret.append(filter(gs, _cadenceWindow)); + GraphSegment &filtered = ret.last(); + + for (int j = 0; j < stop.size(); j++) + filtered[stop.at(j)].setY(0); } - filtered = filter(raw, _cadenceWindow); - - for (int i = 0; i < stop.size(); i++) - filtered[stop.at(i)].setY(0); - - return filtered; + return ret; } Graph Track::power() const { - Graph raw, filtered; + Graph ret; QList stop; qreal p; - for (int i = 0; i < _data.size(); i++) { - if (_data.at(i).hasPower() && _stop.contains(i)) { - p = 0; - stop.append(raw.size()); - } else if (_data.at(i).hasPower() && !_outliers.contains(i)) - p = _data.at(i).power(); - else - continue; - raw.append(GraphPoint(_distance.at(i), _time.at(i), p)); + for (int i = 0; i < _data.size(); i++) { + const SegmentData &segment = _data.at(i); + const Segment &seg = _segments.at(i); + GraphSegment gs; + + for (int j = 0; j < segment.size(); j++) { + if (segment.at(j).hasPower() && seg.stop.contains(j)) { + p = 0; + stop.append(gs.size()); + } else if (segment.at(j).hasPower() && !seg.outliers.contains(j)) + p = segment.at(j).power(); + else + continue; + + gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), p)); + } + + ret.append(filter(gs, _powerWindow)); + GraphSegment &filtered = ret.last(); + + for (int j = 0; j < stop.size(); j++) + filtered[stop.at(j)].setY(0); } - filtered = filter(raw, _powerWindow); - - for (int i = 0; i < stop.size(); i++) - filtered[stop.at(i)].setY(0); - - return filtered; + return ret; } qreal Track::distance() const { - for (int i = _distance.size() - 1; i >= 0; i--) - if (!_outliers.contains(i)) - return _distance.at(i); + for (int i = _segments.size() - 1; i >= 0; i--) { + const Segment &seg = _segments.at(i); + + for (int j = seg.distance.size() - 1; j >= 0; j--) + if (!seg.outliers.contains(j)) + return seg.distance.at(j); + } return 0; } qreal Track::time() const { - for (int i = _data.size() - 1; i >= 0; i--) - if (!_outliers.contains(i)) - return _data.first().timestamp().msecsTo(_data.at(i).timestamp()) - / 1000.0; + for (int i = _segments.size() - 1; i >= 0; i--) { + const Segment &seg = _segments.at(i); + + for (int j = seg.time.size() - 1; j >= 0; j--) + if (!seg.outliers.contains(j)) + return seg.time.at(j); + } return 0; } @@ -314,22 +394,41 @@ qreal Track::movingTime() const QDateTime Track::date() const { - return (_data.size()) ? _data.first().timestamp() : QDateTime(); + return (_data.size() && _data.first().size()) + ? _data.first().first().timestamp() : QDateTime(); } Path Track::path() const { Path ret; - for (int i = 0; i < _data.size(); i++) - if (!_outliers.contains(i) && !discardStopPoint(i)) - ret.append(PathPoint(_data.at(i).coordinates(), _distance.at(i))); + for (int i = 0; i < _data.size(); i++) { + const SegmentData &sd = _data.at(i); + const Segment &seg = _segments.at(i); + ret.append(PathSegment()); + PathSegment &ps = ret.last(); + + for (int j = 0; j < sd.size(); j++) + if (!seg.outliers.contains(j) && !discardStopPoint(seg, j)) + ps.append(PathPoint(sd.at(j).coordinates(), + seg.distance.at(j))); + } return ret; } -bool Track::discardStopPoint(int i) const +bool Track::discardStopPoint(const Segment &seg, int i) const { - return (_stop.contains(i) && i > 0 && _stop.contains(i-1) - && i < _data.size() - 1 && _stop.contains(i+1)); + return (seg.stop.contains(i) && seg.stop.contains(i-1) + && seg.stop.contains(i+1) && i > 0 && i < seg.distance.size() - 1); +} + +bool Track::isValid() const +{ + if (_data.isEmpty()) + return false; + for (int i = 0; i < _data.size(); i++) + if (_data.at(i).size() < 2) + return false; + return true; } diff --git a/src/data/track.h b/src/data/track.h index 6a311f44..d185814c 100644 --- a/src/data/track.h +++ b/src/data/track.h @@ -33,7 +33,7 @@ public: const QString &name() const {return _data.name();} const QString &description() const {return _data.description();} - bool isValid() const {return _data.size() >= 2;} + bool isValid() const; static void setElevationFilter(int window) {_elevationWindow = window;} static void setSpeedFilter(int window) {_speedWindow = window;} @@ -48,17 +48,18 @@ public: static void useDEM(bool use) {_useDEM = use;} private: - bool discardStopPoint(int i) const; + struct Segment { + QVector distance; + QVector time; + QVector speed; + QSet outliers; + QSet stop; + }; + + bool discardStopPoint(const Segment &seg, int i) const; TrackData _data; - - QVector _distance; - QVector _time; - QVector _speed; - - QSet _outliers; - QSet _stop; - + QList _segments; qreal _pause; static bool _outlierEliminate; diff --git a/src/data/trackdata.h b/src/data/trackdata.h index 26604c62..2f2b4953 100644 --- a/src/data/trackdata.h +++ b/src/data/trackdata.h @@ -1,11 +1,14 @@ #ifndef TRACKDATA_H #define TRACKDATA_H +#include #include #include #include "trackpoint.h" -class TrackData : public QVector +typedef QVector SegmentData; + +class TrackData : public QList { public: const QString& name() const {return _name;}