From 923a00479a047435331c6ef5d31dd11774468c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Mon, 8 Feb 2016 17:53:09 +0100 Subject: [PATCH] Added support for GPX files with multiple tracks --- src/elevationgraph.cpp | 51 ++++++++++++++-------------- src/gpx.cpp | 63 +++++++++++++++++++--------------- src/gpx.h | 16 +++++---- src/gui.cpp | 18 ++++++---- src/gui.h | 1 + src/parser.cpp | 62 +++++++++++++++++----------------- src/parser.h | 26 +++++++-------- src/speedgraph.cpp | 25 +++++++------- src/track.cpp | 76 ++++++++++++++++++++++-------------------- 9 files changed, 179 insertions(+), 159 deletions(-) diff --git a/src/elevationgraph.cpp b/src/elevationgraph.cpp index cfee6301..7dc6ea9e 100644 --- a/src/elevationgraph.cpp +++ b/src/elevationgraph.cpp @@ -31,38 +31,39 @@ void ElevationGraph::addInfo() void ElevationGraph::loadGPX(const GPX &gpx) { - QVector data; - qreal min, max, ascent = 0, descent = 0; + for (int i = 0; i < gpx.count(); i++) { + QVector data; + qreal min, max, ascent = 0, descent = 0; + gpx.elevationGraph(i, data); + if (data.isEmpty()) + return; - gpx.elevationGraph(data); - if (data.isEmpty()) - return; + min = max = data.at(0).y(); - min = max = data.at(0).y(); + for (int i = 1; i < data.size(); i++) { + qreal cur = data.at(i).y(); + qreal prev = data.at(i-1).y(); - for (int i = 1; i < data.size(); i++) { - qreal cur = data.at(i).y(); - qreal prev = data.at(i-1).y(); + if (cur > prev) + ascent += cur - prev; + if (cur < prev) + descent += prev - cur; - if (cur > prev) - ascent += cur - prev; - if (cur < prev) - descent += prev - cur; + if (cur > max) + max = cur; + if (cur < min) + min = cur; + } - if (cur > max) - max = cur; - if (cur < min) - min = cur; + _ascent += ascent; + _descent += descent; + _max = qMax(_max, max); + _min = qMin(_min, min); + + addInfo(); + loadData(data); } - - _ascent += ascent; - _descent += descent; - _max = qMax(_max, max); - _min = qMin(_min, min); - - addInfo(); - loadData(data); } void ElevationGraph::clear() diff --git a/src/gpx.cpp b/src/gpx.cpp index fac27edf..80175fae 100644 --- a/src/gpx.cpp +++ b/src/gpx.cpp @@ -94,7 +94,7 @@ bool GPX::loadFile(const QString &fileName) return false; } - if (!(ret = _parser.loadFile(&file, _data))) { + if (!(ret = _parser.loadFile(&file, &_data))) { _error = _parser.errorString(); _errorLine = _parser.errorLine(); } @@ -103,44 +103,46 @@ bool GPX::loadFile(const QString &fileName) return ret; } -void GPX::elevationGraph(QVector &graph) const +void GPX::elevationGraph(int i, QVector &graph) const { + const QVector &data = _data.at(i); qreal dist = 0; QVector raw; - if (!_data.size()) + if (!data.size()) return; - raw.append(QPointF(0, _data.at(0).elevation)); - for (int i = 1; i < _data.size(); i++) { - dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); - raw.append(QPointF(dist, _data.at(i).elevation - - _data.at(i).geoidheight)); + raw.append(QPointF(0, data.at(0).elevation)); + for (int i = 1; i < data.size(); i++) { + dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates); + raw.append(QPointF(dist, data.at(i).elevation + - data.at(i).geoidheight)); } graph = filter(raw, WINDOW_EF); } -void GPX::speedGraph(QVector &graph) const +void GPX::speedGraph(int i, QVector &graph) const { + const QVector &data = _data.at(i); qreal dist = 0, v, ds, dt; QVector raw; - if (!_data.size()) + if (!data.size()) return; raw.append(QPointF(0, 0)); - for (int i = 1; i < _data.size(); i++) { - ds = llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); - dt = _data.at(i-1).timestamp.msecsTo(_data.at(i).timestamp) / 1000.0; + for (int i = 1; i < data.size(); i++) { + ds = llDistance(data.at(i).coordinates, data.at(i-1).coordinates); + dt = data.at(i-1).timestamp.msecsTo(data.at(i).timestamp) / 1000.0; dist += ds; - if (_data.at(i).speed < 0) { + if (data.at(i).speed < 0) { if (dt == 0) continue; v = ds / dt; } else - v = _data.at(i).speed; + v = data.at(i).speed; raw.append(QPointF(dist, v)); } @@ -148,35 +150,42 @@ void GPX::speedGraph(QVector &graph) const graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF); } -void GPX::track(QVector &track) const +void GPX::track(int i, QVector &track) const { - for (int i = 0; i < _data.size(); i++) - track.append(ll2mercator(_data.at(i).coordinates)); + const QVector &data = _data.at(i); + + for (int i = 0; i < data.size(); i++) + track.append(ll2mercator(data.at(i).coordinates)); } -qreal GPX::distance() const +qreal GPX::distance(int i) const { + const QVector &data = _data.at(i); qreal dist = 0; - for (int i = 1; i < _data.size(); i++) - dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); + for (int i = 1; i < data.size(); i++) + dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates); return dist; } -qreal GPX::time() const +qreal GPX::time(int i) const { - if (_data.size() < 2) + const QVector &data = _data.at(i); + + if (data.size() < 2) return 0; - return (_data.at(0).timestamp.msecsTo(_data.at(_data.size() - 1).timestamp) + return (data.at(0).timestamp.msecsTo(data.at(data.size() - 1).timestamp) / 1000.0); } -QDateTime GPX::date() const +QDateTime GPX::date(int i) const { - if (_data.size()) - return _data.at(0).timestamp; + const QVector &data = _data.at(i); + + if (data.size()) + return data.at(0).timestamp; else return QDateTime(); } diff --git a/src/gpx.h b/src/gpx.h index 0258ff9b..85b54552 100644 --- a/src/gpx.h +++ b/src/gpx.h @@ -2,6 +2,7 @@ #define GPX_H #include +#include #include #include #include "parser.h" @@ -13,16 +14,17 @@ public: const QString &errorString() const {return _error;} int errorLine() const {return _errorLine;} - void elevationGraph(QVector &graph) const; - void speedGraph(QVector &graph) const; - void track(QVector &track) const; - qreal distance() const; - qreal time() const; - QDateTime date() const; + int count() const {return _data.count();} + void elevationGraph(int i, QVector &graph) const; + void speedGraph(int i, QVector &graph) const; + void track(int i, QVector &track) const; + qreal distance(int i) const; + qreal time(int i) const; + QDateTime date(int i) const; private: Parser _parser; - QVector _data; + QList > _data; QString _error; int _errorLine; }; diff --git a/src/gui.cpp b/src/gui.cpp index 3ce039a8..7bcbd9fc 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -67,6 +67,7 @@ GUI::GUI() _distance = 0; _time = 0; + _trackCount = 0; resize(600, 800); } @@ -443,8 +444,12 @@ bool GUI::loadFile(const QString &fileName) if (_showPOIAction->isChecked()) _track->loadPOI(_poi); - _distance += gpx.distance(); - _time += gpx.time(); + for (int i = 0; i < gpx.count(); i++) { + _distance += gpx.distance(i); + _time += gpx.time(i); + } + + _trackCount += gpx.count(); return true; } else { @@ -571,6 +576,7 @@ void GUI::closeFile() { _distance = 0; _time = 0; + _trackCount = 0; _elevationGraph->clear(); _speedGraph->clear(); @@ -621,17 +627,15 @@ void GUI::showToolbars(bool checked) void GUI::updateStatusBarInfo() { - int files = _files.size(); - - if (files == 0) { + if (_files.count() == 0) { _fileNameLabel->clear(); _distanceLabel->clear(); _timeLabel->clear(); return; - } else if (files == 1) + } else if (_files.count() == 1) _fileNameLabel->setText(_files.at(0)); else - _fileNameLabel->setText(tr("%1 tracks").arg(_files.size())); + _fileNameLabel->setText(tr("%1 tracks").arg(_trackCount)); if (_imperialUnitsAction->isChecked()) _distanceLabel->setText(QString::number(_distance * M2MI, 'f', 1) diff --git a/src/gui.h b/src/gui.h index ec98b094..61961b8a 100644 --- a/src/gui.h +++ b/src/gui.h @@ -126,6 +126,7 @@ private: qreal _distance; qreal _time; + int _trackCount; }; #endif // GUI_H diff --git a/src/parser.cpp b/src/parser.cpp index 2e332794..ea1ed8d7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,96 +1,95 @@ #include "parser.h" -void Parser::handleExtensionData(QVector &data, - QStringRef element, const QString &value) +void Parser::handleExtensionData(QStringRef element, const QString &value) { if (element == "speed") - data.last().speed = value.toDouble(); + _track->last().speed = value.toDouble(); } -void Parser::handleTrekPointData(QVector &data, - QStringRef element, const QString &value) +void Parser::handleTrekPointData(QStringRef element, const QString &value) { if (element == "ele") - data.last().elevation = value.toLatin1().toDouble(); + _track->last().elevation = value.toLatin1().toDouble(); if (element == "time") - data.last().timestamp = QDateTime::fromString(value.toLatin1(), + _track->last().timestamp = QDateTime::fromString(value.toLatin1(), Qt::ISODate); if (element == "geoidheight") - data.last().geoidheight = value.toLatin1().toDouble(); + _track->last().geoidheight = value.toLatin1().toDouble(); } -void Parser::handleTrekPointAttributes(QVector &data, - const QXmlStreamAttributes &attr) +void Parser::handleTrekPointAttributes(const QXmlStreamAttributes &attr) { - data.last().coordinates.setY(attr.value("lat").toLatin1().toDouble()); - data.last().coordinates.setX(attr.value("lon").toLatin1().toDouble()); + _track->last().coordinates.setY(attr.value("lat").toLatin1().toDouble()); + _track->last().coordinates.setX(attr.value("lon").toLatin1().toDouble()); } -void Parser::extensions(QVector &data) +void Parser::extensions() { while (_reader.readNextStartElement()) { if (_reader.name() == "speed") - handleExtensionData(data, _reader.name(), _reader.readElementText()); + handleExtensionData(_reader.name(), _reader.readElementText()); else _reader.skipCurrentElement(); } } -void Parser::trekPointData(QVector &data) +void Parser::trackPointData() { while (_reader.readNextStartElement()) { if (_reader.name() == "ele" || _reader.name() == "time" || _reader.name() == "geoidheight") - handleTrekPointData(data, _reader.name(), _reader.readElementText()); + handleTrekPointData(_reader.name(), _reader.readElementText()); else if (_reader.name() == "extensions") - extensions(data); + extensions(); else _reader.skipCurrentElement(); } } -void Parser::trekPoints(QVector &data) +void Parser::trackPoints() { QXmlStreamAttributes attr; while (_reader.readNextStartElement()) { if (_reader.name() == "trkpt") { attr = _reader.attributes(); - data.append(TrackPoint()); - handleTrekPointAttributes(data, attr); - trekPointData(data); + _track->append(TrackPoint()); + handleTrekPointAttributes(attr); + trackPointData(); } else _reader.skipCurrentElement(); } } -void Parser::trek(QVector &data) +void Parser::track() { while (_reader.readNextStartElement()) { if (_reader.name() == "trkseg") { - trekPoints(data); + trackPoints(); } else _reader.skipCurrentElement(); } } -void Parser::gpx(QVector &data) +void Parser::gpx() { while (_reader.readNextStartElement()) { - if (_reader.name() == "trk") - trek(data); - else + if (_reader.name() == "trk") { + _data->append(QVector()); + _track = &_data->back(); + track(); + } else _reader.skipCurrentElement(); } } -bool Parser::parse(QVector &data) +bool Parser::parse() { if (_reader.readNextStartElement()) { if (_reader.name() == "gpx") - gpx(data); + gpx(); else _reader.raiseError("Not a GPX file."); } @@ -98,10 +97,11 @@ bool Parser::parse(QVector &data) return !_reader.error(); } -bool Parser::loadFile(QIODevice *device, QVector &data) +bool Parser::loadFile(QIODevice *device, QList > *data) { _reader.clear(); _reader.setDevice(device); + _data = data; - return parse(data); + return parse(); } diff --git a/src/parser.h b/src/parser.h index 567449c2..b272037c 100644 --- a/src/parser.h +++ b/src/parser.h @@ -19,26 +19,26 @@ struct TrackPoint class Parser { public: - bool loadFile(QIODevice *device, QVector &data); + Parser() {_data = 0; _track = 0;} + bool loadFile(QIODevice *device, QList > *data); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} private: - bool parse(QVector &data); - void gpx(QVector &data); - void trek(QVector &data); - void trekPoints(QVector &data); - void extensions(QVector &data); - void trekPointData(QVector &data); + bool parse(); + void gpx(); + void track(); + void trackPoints(); + void extensions(); + void trackPointData(); - void handleTrekPointAttributes(QVector &data, - const QXmlStreamAttributes &attr); - void handleTrekPointData(QVector &data, QStringRef element, - const QString &value); - void handleExtensionData(QVector &data, QStringRef element, - const QString &value); + void handleTrekPointAttributes(const QXmlStreamAttributes &attr); + void handleTrekPointData(QStringRef element, const QString &value); + void handleExtensionData(QStringRef element, const QString &value); QXmlStreamReader _reader; + QList > *_data; + QVector *_track; }; #endif // PARSER_H diff --git a/src/speedgraph.cpp b/src/speedgraph.cpp index 22d50280..6ab70463 100644 --- a/src/speedgraph.cpp +++ b/src/speedgraph.cpp @@ -25,22 +25,23 @@ void SpeedGraph::addInfo() void SpeedGraph::loadGPX(const GPX &gpx) { - QVector data; - qreal max = 0; + for (int i = 0; i < gpx.count(); i++) { + QVector data; + qreal max = 0; + gpx.speedGraph(i, data); + if (data.isEmpty()) + return; - gpx.speedGraph(data); - if (data.isEmpty()) - return; + _avg.append(QPointF(gpx.distance(i), gpx.distance(i) / gpx.time(i))); - _avg.append(QPointF(gpx.distance(), gpx.distance() / gpx.time())); + for (int i = 0; i < data.size(); i++) + max = qMax(max, data.at(i).y()); + _max = qMax(_max, max); - for (int i = 0; i < data.size(); i++) - max = qMax(max, data.at(i).y()); - _max = qMax(_max, max); - - addInfo(); - loadData(data); + addInfo(); + loadData(data); + } } qreal SpeedGraph::avg() const diff --git a/src/track.cpp b/src/track.cpp index 94949f70..9b85b8ba 100644 --- a/src/track.cpp +++ b/src/track.cpp @@ -41,56 +41,58 @@ Track::~Track() void Track::loadGPX(const GPX &gpx) { - QVector track; - QPainterPath path; - QGraphicsPathItem *pi; - MarkerItem *mi; - QColor color = _colorShop.color(); - qreal prevScale = _scale; + for (int i = 0; i < gpx.count(); i++) { + QVector track; + QPainterPath path; + QGraphicsPathItem *pi; + MarkerItem *mi; + QColor color = _colorShop.color(); + qreal prevScale = _scale; - gpx.track(track); + gpx.track(i, track); - if (track.size() < 2) - return; + if (track.size() < 2) + continue; - _tracks.append(track); + _tracks.append(track); - path.moveTo(track.at(0).x(), -track.at(0).y()); - for (int i = 1; i < track.size(); i++) - path.lineTo(track.at(i).x(), -track.at(i).y()); + path.moveTo(track.at(0).x(), -track.at(0).y()); + for (int i = 1; i < track.size(); i++) + path.lineTo(track.at(i).x(), -track.at(i).y()); - _maxLen = qMax(path.length(), _maxLen); + _maxLen = qMax(path.length(), _maxLen); - pi = new QGraphicsPathItem(path); - _trackPaths.append(pi); - _zoom = scale2zoom(trackScale()); - _scale = mapScale(); - QBrush brush(color, Qt::SolidPattern); - QPen pen(brush, TRACK_WIDTH * _scale); - pi->setPen(pen); - pi->setScale(1.0/_scale); - _scene->addItem(pi); + pi = new QGraphicsPathItem(path); + _trackPaths.append(pi); + _zoom = scale2zoom(trackScale()); + _scale = mapScale(); + QBrush brush(color, Qt::SolidPattern); + QPen pen(brush, TRACK_WIDTH * _scale); + pi->setPen(pen); + pi->setScale(1.0/_scale); + _scene->addItem(pi); - mi = new MarkerItem(pi); - _markers.append(mi); - mi->setPos(pi->path().pointAtPercent(0)); - mi->setScale(_scale); + mi = new MarkerItem(pi); + _markers.append(mi); + mi->setPos(pi->path().pointAtPercent(0)); + mi->setScale(_scale); - if (_trackPaths.size() > 1 && prevScale != _scale) - rescale(_scale); + if (_trackPaths.size() > 1 && prevScale != _scale) + rescale(_scale); - QRectF br = trackBoundingRect(); - QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE); - _scene->setSceneRect(ba); - centerOn(ba.center()); + QRectF br = trackBoundingRect(); + QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE); + _scene->setSceneRect(ba); + centerOn(ba.center()); - if (_mapScale->scene() != _scene) - _scene->addItem(_mapScale); + if (_mapScale->scene() != _scene) + _scene->addItem(_mapScale); - _mapScale->setLatitude(track.at(track.size() / 2).y()); - _mapScale->setZoom(_zoom); + _mapScale->setLatitude(track.at(track.size() / 2).y()); + _mapScale->setZoom(_zoom); + } } QRectF Track::trackBoundingRect() const