1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-02-20 09:40:49 +01:00

Added support for GPX files with multiple tracks

This commit is contained in:
Martin Tůma 2016-02-08 17:53:09 +01:00
parent 82660caccb
commit 923a00479a
9 changed files with 179 additions and 159 deletions

View File

@ -31,38 +31,39 @@ void ElevationGraph::addInfo()
void ElevationGraph::loadGPX(const GPX &gpx) void ElevationGraph::loadGPX(const GPX &gpx)
{ {
QVector<QPointF> data; for (int i = 0; i < gpx.count(); i++) {
qreal min, max, ascent = 0, descent = 0; QVector<QPointF> data;
qreal min, max, ascent = 0, descent = 0;
gpx.elevationGraph(i, data);
if (data.isEmpty())
return;
gpx.elevationGraph(data); min = max = data.at(0).y();
if (data.isEmpty())
return;
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++) { if (cur > prev)
qreal cur = data.at(i).y(); ascent += cur - prev;
qreal prev = data.at(i-1).y(); if (cur < prev)
descent += prev - cur;
if (cur > prev) if (cur > max)
ascent += cur - prev; max = cur;
if (cur < prev) if (cur < min)
descent += prev - cur; min = cur;
}
if (cur > max) _ascent += ascent;
max = cur; _descent += descent;
if (cur < min) _max = qMax(_max, max);
min = cur; _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() void ElevationGraph::clear()

View File

@ -94,7 +94,7 @@ bool GPX::loadFile(const QString &fileName)
return false; return false;
} }
if (!(ret = _parser.loadFile(&file, _data))) { if (!(ret = _parser.loadFile(&file, &_data))) {
_error = _parser.errorString(); _error = _parser.errorString();
_errorLine = _parser.errorLine(); _errorLine = _parser.errorLine();
} }
@ -103,44 +103,46 @@ bool GPX::loadFile(const QString &fileName)
return ret; return ret;
} }
void GPX::elevationGraph(QVector<QPointF> &graph) const void GPX::elevationGraph(int i, QVector<QPointF> &graph) const
{ {
const QVector<TrackPoint> &data = _data.at(i);
qreal dist = 0; qreal dist = 0;
QVector<QPointF> raw; QVector<QPointF> raw;
if (!_data.size()) if (!data.size())
return; return;
raw.append(QPointF(0, _data.at(0).elevation)); raw.append(QPointF(0, data.at(0).elevation));
for (int i = 1; i < _data.size(); i++) { for (int i = 1; i < data.size(); i++) {
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
raw.append(QPointF(dist, _data.at(i).elevation raw.append(QPointF(dist, data.at(i).elevation
- _data.at(i).geoidheight)); - data.at(i).geoidheight));
} }
graph = filter(raw, WINDOW_EF); graph = filter(raw, WINDOW_EF);
} }
void GPX::speedGraph(QVector<QPointF> &graph) const void GPX::speedGraph(int i, QVector<QPointF> &graph) const
{ {
const QVector<TrackPoint> &data = _data.at(i);
qreal dist = 0, v, ds, dt; qreal dist = 0, v, ds, dt;
QVector<QPointF> raw; QVector<QPointF> raw;
if (!_data.size()) if (!data.size())
return; return;
raw.append(QPointF(0, 0)); raw.append(QPointF(0, 0));
for (int i = 1; i < _data.size(); i++) { for (int i = 1; i < data.size(); i++) {
ds = llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); ds = llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
dt = _data.at(i-1).timestamp.msecsTo(_data.at(i).timestamp) / 1000.0; dt = data.at(i-1).timestamp.msecsTo(data.at(i).timestamp) / 1000.0;
dist += ds; dist += ds;
if (_data.at(i).speed < 0) { if (data.at(i).speed < 0) {
if (dt == 0) if (dt == 0)
continue; continue;
v = ds / dt; v = ds / dt;
} else } else
v = _data.at(i).speed; v = data.at(i).speed;
raw.append(QPointF(dist, v)); raw.append(QPointF(dist, v));
} }
@ -148,35 +150,42 @@ void GPX::speedGraph(QVector<QPointF> &graph) const
graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF); graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
} }
void GPX::track(QVector<QPointF> &track) const void GPX::track(int i, QVector<QPointF> &track) const
{ {
for (int i = 0; i < _data.size(); i++) const QVector<TrackPoint> &data = _data.at(i);
track.append(ll2mercator(_data.at(i).coordinates));
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<TrackPoint> &data = _data.at(i);
qreal dist = 0; qreal dist = 0;
for (int i = 1; i < _data.size(); i++) for (int i = 1; i < data.size(); i++)
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates); dist += llDistance(data.at(i).coordinates, data.at(i-1).coordinates);
return dist; return dist;
} }
qreal GPX::time() const qreal GPX::time(int i) const
{ {
if (_data.size() < 2) const QVector<TrackPoint> &data = _data.at(i);
if (data.size() < 2)
return 0; 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); / 1000.0);
} }
QDateTime GPX::date() const QDateTime GPX::date(int i) const
{ {
if (_data.size()) const QVector<TrackPoint> &data = _data.at(i);
return _data.at(0).timestamp;
if (data.size())
return data.at(0).timestamp;
else else
return QDateTime(); return QDateTime();
} }

View File

@ -2,6 +2,7 @@
#define GPX_H #define GPX_H
#include <QVector> #include <QVector>
#include <QList>
#include <QPointF> #include <QPointF>
#include <QString> #include <QString>
#include "parser.h" #include "parser.h"
@ -13,16 +14,17 @@ public:
const QString &errorString() const {return _error;} const QString &errorString() const {return _error;}
int errorLine() const {return _errorLine;} int errorLine() const {return _errorLine;}
void elevationGraph(QVector<QPointF> &graph) const; int count() const {return _data.count();}
void speedGraph(QVector<QPointF> &graph) const; void elevationGraph(int i, QVector<QPointF> &graph) const;
void track(QVector<QPointF> &track) const; void speedGraph(int i, QVector<QPointF> &graph) const;
qreal distance() const; void track(int i, QVector<QPointF> &track) const;
qreal time() const; qreal distance(int i) const;
QDateTime date() const; qreal time(int i) const;
QDateTime date(int i) const;
private: private:
Parser _parser; Parser _parser;
QVector<TrackPoint> _data; QList<QVector<TrackPoint> > _data;
QString _error; QString _error;
int _errorLine; int _errorLine;
}; };

View File

@ -67,6 +67,7 @@ GUI::GUI()
_distance = 0; _distance = 0;
_time = 0; _time = 0;
_trackCount = 0;
resize(600, 800); resize(600, 800);
} }
@ -443,8 +444,12 @@ bool GUI::loadFile(const QString &fileName)
if (_showPOIAction->isChecked()) if (_showPOIAction->isChecked())
_track->loadPOI(_poi); _track->loadPOI(_poi);
_distance += gpx.distance(); for (int i = 0; i < gpx.count(); i++) {
_time += gpx.time(); _distance += gpx.distance(i);
_time += gpx.time(i);
}
_trackCount += gpx.count();
return true; return true;
} else { } else {
@ -571,6 +576,7 @@ void GUI::closeFile()
{ {
_distance = 0; _distance = 0;
_time = 0; _time = 0;
_trackCount = 0;
_elevationGraph->clear(); _elevationGraph->clear();
_speedGraph->clear(); _speedGraph->clear();
@ -621,17 +627,15 @@ void GUI::showToolbars(bool checked)
void GUI::updateStatusBarInfo() void GUI::updateStatusBarInfo()
{ {
int files = _files.size(); if (_files.count() == 0) {
if (files == 0) {
_fileNameLabel->clear(); _fileNameLabel->clear();
_distanceLabel->clear(); _distanceLabel->clear();
_timeLabel->clear(); _timeLabel->clear();
return; return;
} else if (files == 1) } else if (_files.count() == 1)
_fileNameLabel->setText(_files.at(0)); _fileNameLabel->setText(_files.at(0));
else else
_fileNameLabel->setText(tr("%1 tracks").arg(_files.size())); _fileNameLabel->setText(tr("%1 tracks").arg(_trackCount));
if (_imperialUnitsAction->isChecked()) if (_imperialUnitsAction->isChecked())
_distanceLabel->setText(QString::number(_distance * M2MI, 'f', 1) _distanceLabel->setText(QString::number(_distance * M2MI, 'f', 1)

View File

@ -126,6 +126,7 @@ private:
qreal _distance; qreal _distance;
qreal _time; qreal _time;
int _trackCount;
}; };
#endif // GUI_H #endif // GUI_H

View File

@ -1,96 +1,95 @@
#include "parser.h" #include "parser.h"
void Parser::handleExtensionData(QVector<TrackPoint> &data, void Parser::handleExtensionData(QStringRef element, const QString &value)
QStringRef element, const QString &value)
{ {
if (element == "speed") if (element == "speed")
data.last().speed = value.toDouble(); _track->last().speed = value.toDouble();
} }
void Parser::handleTrekPointData(QVector<TrackPoint> &data, void Parser::handleTrekPointData(QStringRef element, const QString &value)
QStringRef element, const QString &value)
{ {
if (element == "ele") if (element == "ele")
data.last().elevation = value.toLatin1().toDouble(); _track->last().elevation = value.toLatin1().toDouble();
if (element == "time") if (element == "time")
data.last().timestamp = QDateTime::fromString(value.toLatin1(), _track->last().timestamp = QDateTime::fromString(value.toLatin1(),
Qt::ISODate); Qt::ISODate);
if (element == "geoidheight") if (element == "geoidheight")
data.last().geoidheight = value.toLatin1().toDouble(); _track->last().geoidheight = value.toLatin1().toDouble();
} }
void Parser::handleTrekPointAttributes(QVector<TrackPoint> &data, void Parser::handleTrekPointAttributes(const QXmlStreamAttributes &attr)
const QXmlStreamAttributes &attr)
{ {
data.last().coordinates.setY(attr.value("lat").toLatin1().toDouble()); _track->last().coordinates.setY(attr.value("lat").toLatin1().toDouble());
data.last().coordinates.setX(attr.value("lon").toLatin1().toDouble()); _track->last().coordinates.setX(attr.value("lon").toLatin1().toDouble());
} }
void Parser::extensions(QVector<TrackPoint> &data) void Parser::extensions()
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "speed") if (_reader.name() == "speed")
handleExtensionData(data, _reader.name(), _reader.readElementText()); handleExtensionData(_reader.name(), _reader.readElementText());
else else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
void Parser::trekPointData(QVector<TrackPoint> &data) void Parser::trackPointData()
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "ele" || _reader.name() == "time" if (_reader.name() == "ele" || _reader.name() == "time"
|| _reader.name() == "geoidheight") || _reader.name() == "geoidheight")
handleTrekPointData(data, _reader.name(), _reader.readElementText()); handleTrekPointData(_reader.name(), _reader.readElementText());
else if (_reader.name() == "extensions") else if (_reader.name() == "extensions")
extensions(data); extensions();
else else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
void Parser::trekPoints(QVector<TrackPoint> &data) void Parser::trackPoints()
{ {
QXmlStreamAttributes attr; QXmlStreamAttributes attr;
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "trkpt") { if (_reader.name() == "trkpt") {
attr = _reader.attributes(); attr = _reader.attributes();
data.append(TrackPoint()); _track->append(TrackPoint());
handleTrekPointAttributes(data, attr); handleTrekPointAttributes(attr);
trekPointData(data); trackPointData();
} else } else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
void Parser::trek(QVector<TrackPoint> &data) void Parser::track()
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "trkseg") { if (_reader.name() == "trkseg") {
trekPoints(data); trackPoints();
} else } else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
void Parser::gpx(QVector<TrackPoint> &data) void Parser::gpx()
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == "trk") if (_reader.name() == "trk") {
trek(data); _data->append(QVector<TrackPoint>());
else _track = &_data->back();
track();
} else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
bool Parser::parse(QVector<TrackPoint> &data) bool Parser::parse()
{ {
if (_reader.readNextStartElement()) { if (_reader.readNextStartElement()) {
if (_reader.name() == "gpx") if (_reader.name() == "gpx")
gpx(data); gpx();
else else
_reader.raiseError("Not a GPX file."); _reader.raiseError("Not a GPX file.");
} }
@ -98,10 +97,11 @@ bool Parser::parse(QVector<TrackPoint> &data)
return !_reader.error(); return !_reader.error();
} }
bool Parser::loadFile(QIODevice *device, QVector<TrackPoint> &data) bool Parser::loadFile(QIODevice *device, QList<QVector<TrackPoint> > *data)
{ {
_reader.clear(); _reader.clear();
_reader.setDevice(device); _reader.setDevice(device);
_data = data;
return parse(data); return parse();
} }

View File

@ -19,26 +19,26 @@ struct TrackPoint
class Parser class Parser
{ {
public: public:
bool loadFile(QIODevice *device, QVector<TrackPoint> &data); Parser() {_data = 0; _track = 0;}
bool loadFile(QIODevice *device, QList<QVector<TrackPoint> > *data);
QString errorString() const {return _reader.errorString();} QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();} int errorLine() const {return _reader.lineNumber();}
private: private:
bool parse(QVector<TrackPoint> &data); bool parse();
void gpx(QVector<TrackPoint> &data); void gpx();
void trek(QVector<TrackPoint> &data); void track();
void trekPoints(QVector<TrackPoint> &data); void trackPoints();
void extensions(QVector<TrackPoint> &data); void extensions();
void trekPointData(QVector<TrackPoint> &data); void trackPointData();
void handleTrekPointAttributes(QVector<TrackPoint> &data, void handleTrekPointAttributes(const QXmlStreamAttributes &attr);
const QXmlStreamAttributes &attr); void handleTrekPointData(QStringRef element, const QString &value);
void handleTrekPointData(QVector<TrackPoint> &data, QStringRef element, void handleExtensionData(QStringRef element, const QString &value);
const QString &value);
void handleExtensionData(QVector<TrackPoint> &data, QStringRef element,
const QString &value);
QXmlStreamReader _reader; QXmlStreamReader _reader;
QList<QVector<TrackPoint> > *_data;
QVector<TrackPoint> *_track;
}; };
#endif // PARSER_H #endif // PARSER_H

View File

@ -25,22 +25,23 @@ void SpeedGraph::addInfo()
void SpeedGraph::loadGPX(const GPX &gpx) void SpeedGraph::loadGPX(const GPX &gpx)
{ {
QVector<QPointF> data; for (int i = 0; i < gpx.count(); i++) {
qreal max = 0; QVector<QPointF> data;
qreal max = 0;
gpx.speedGraph(i, data);
if (data.isEmpty())
return;
gpx.speedGraph(data); _avg.append(QPointF(gpx.distance(i), gpx.distance(i) / gpx.time(i)));
if (data.isEmpty())
return;
_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++) addInfo();
max = qMax(max, data.at(i).y()); loadData(data);
_max = qMax(_max, max); }
addInfo();
loadData(data);
} }
qreal SpeedGraph::avg() const qreal SpeedGraph::avg() const

View File

@ -41,56 +41,58 @@ Track::~Track()
void Track::loadGPX(const GPX &gpx) void Track::loadGPX(const GPX &gpx)
{ {
QVector<QPointF> track; for (int i = 0; i < gpx.count(); i++) {
QPainterPath path; QVector<QPointF> track;
QGraphicsPathItem *pi; QPainterPath path;
MarkerItem *mi; QGraphicsPathItem *pi;
QColor color = _colorShop.color(); MarkerItem *mi;
qreal prevScale = _scale; QColor color = _colorShop.color();
qreal prevScale = _scale;
gpx.track(track); gpx.track(i, track);
if (track.size() < 2) if (track.size() < 2)
return; continue;
_tracks.append(track); _tracks.append(track);
path.moveTo(track.at(0).x(), -track.at(0).y()); path.moveTo(track.at(0).x(), -track.at(0).y());
for (int i = 1; i < track.size(); i++) for (int i = 1; i < track.size(); i++)
path.lineTo(track.at(i).x(), -track.at(i).y()); path.lineTo(track.at(i).x(), -track.at(i).y());
_maxLen = qMax(path.length(), _maxLen); _maxLen = qMax(path.length(), _maxLen);
pi = new QGraphicsPathItem(path); pi = new QGraphicsPathItem(path);
_trackPaths.append(pi); _trackPaths.append(pi);
_zoom = scale2zoom(trackScale()); _zoom = scale2zoom(trackScale());
_scale = mapScale(); _scale = mapScale();
QBrush brush(color, Qt::SolidPattern); QBrush brush(color, Qt::SolidPattern);
QPen pen(brush, TRACK_WIDTH * _scale); QPen pen(brush, TRACK_WIDTH * _scale);
pi->setPen(pen); pi->setPen(pen);
pi->setScale(1.0/_scale); pi->setScale(1.0/_scale);
_scene->addItem(pi); _scene->addItem(pi);
mi = new MarkerItem(pi); mi = new MarkerItem(pi);
_markers.append(mi); _markers.append(mi);
mi->setPos(pi->path().pointAtPercent(0)); mi->setPos(pi->path().pointAtPercent(0));
mi->setScale(_scale); mi->setScale(_scale);
if (_trackPaths.size() > 1 && prevScale != _scale) if (_trackPaths.size() > 1 && prevScale != _scale)
rescale(_scale); rescale(_scale);
QRectF br = trackBoundingRect(); QRectF br = trackBoundingRect();
QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE); QRectF ba = br.adjusted(-TILE_SIZE, -TILE_SIZE, TILE_SIZE, TILE_SIZE);
_scene->setSceneRect(ba); _scene->setSceneRect(ba);
centerOn(ba.center()); centerOn(ba.center());
if (_mapScale->scene() != _scene) if (_mapScale->scene() != _scene)
_scene->addItem(_mapScale); _scene->addItem(_mapScale);
_mapScale->setLatitude(track.at(track.size() / 2).y()); _mapScale->setLatitude(track.at(track.size() / 2).y());
_mapScale->setZoom(_zoom); _mapScale->setZoom(_zoom);
}
} }
QRectF Track::trackBoundingRect() const QRectF Track::trackBoundingRect() const