2016-10-23 11:09:20 +02:00
|
|
|
#include "tcxparser.h"
|
|
|
|
|
|
|
|
|
2016-11-02 17:33:06 +01:00
|
|
|
void TCXParser::warning(const char *text) const
|
2016-10-29 10:40:30 +02:00
|
|
|
{
|
2016-11-02 17:33:06 +01:00
|
|
|
const QFile *file = static_cast<QFile *>(_reader.device());
|
2017-03-18 01:30:31 +01:00
|
|
|
qWarning("%s:%lld: %s\n", qPrintable(file->fileName()),
|
2016-10-29 10:40:30 +02:00
|
|
|
_reader.lineNumber(), text);
|
|
|
|
}
|
|
|
|
|
2016-10-27 09:15:05 +02:00
|
|
|
qreal TCXParser::number()
|
|
|
|
{
|
|
|
|
bool res;
|
|
|
|
qreal ret = _reader.readElementText().toDouble(&res);
|
|
|
|
if (!res)
|
2016-10-29 12:22:28 +02:00
|
|
|
_reader.raiseError(QString("Invalid %1").arg(
|
2016-10-27 09:15:05 +02:00
|
|
|
_reader.name().toString()));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime TCXParser::time()
|
|
|
|
{
|
|
|
|
QDateTime d = QDateTime::fromString(_reader.readElementText(),
|
|
|
|
Qt::ISODate);
|
|
|
|
if (!d.isValid())
|
2016-10-29 12:22:28 +02:00
|
|
|
_reader.raiseError(QString("Invalid %1").arg(
|
2016-10-27 09:15:05 +02:00
|
|
|
_reader.name().toString()));
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2016-10-24 00:21:40 +02:00
|
|
|
Coordinates TCXParser::position()
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
2016-10-24 00:21:40 +02:00
|
|
|
Coordinates pos;
|
|
|
|
qreal val;
|
|
|
|
bool res;
|
2016-10-23 11:09:20 +02:00
|
|
|
|
|
|
|
while (_reader.readNextStartElement()) {
|
2016-10-24 00:21:40 +02:00
|
|
|
if (_reader.name() == "LatitudeDegrees") {
|
|
|
|
val = _reader.readElementText().toDouble(&res);
|
|
|
|
if (!res || (val < -90.0 || val > 90.0))
|
2016-10-29 12:22:28 +02:00
|
|
|
_reader.raiseError("Invalid LatitudeDegrees");
|
2016-10-24 00:21:40 +02:00
|
|
|
else
|
2016-10-24 00:51:19 +02:00
|
|
|
pos.setLat(val);
|
2016-10-24 00:21:40 +02:00
|
|
|
} else if (_reader.name() == "LongitudeDegrees") {
|
|
|
|
val = _reader.readElementText().toDouble(&res);
|
|
|
|
if (!res || (val < -180.0 || val > 180.0))
|
2016-10-29 12:22:28 +02:00
|
|
|
_reader.raiseError("Invalid LongitudeDegrees");
|
2016-10-24 00:21:40 +02:00
|
|
|
else
|
2016-10-24 00:51:19 +02:00
|
|
|
pos.setLon(val);
|
2016-10-24 00:21:40 +02:00
|
|
|
} else
|
2016-10-23 11:09:20 +02:00
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2016-11-06 03:28:08 +01:00
|
|
|
void TCXParser::extensions(Trackpoint &trackpoint)
|
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "RunCadence")
|
|
|
|
trackpoint.setCadence(number());
|
|
|
|
else if (_reader.name() == "Watts")
|
|
|
|
trackpoint.setPower(number());
|
|
|
|
else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-27 09:15:05 +02:00
|
|
|
void TCXParser::trackpointData(Trackpoint &trackpoint)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "Position")
|
2016-10-27 09:15:05 +02:00
|
|
|
trackpoint.setCoordinates(position());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "AltitudeMeters")
|
2016-10-27 09:15:05 +02:00
|
|
|
trackpoint.setElevation(number());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "Time")
|
2016-10-27 09:15:05 +02:00
|
|
|
trackpoint.setTimestamp(time());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "HeartRateBpm")
|
2016-10-27 09:15:05 +02:00
|
|
|
trackpoint.setHeartRate(number());
|
2016-11-06 03:28:08 +01:00
|
|
|
else if (_reader.name() == "Cadence")
|
|
|
|
trackpoint.setCadence(number());
|
|
|
|
else if (_reader.name() == "Extensions")
|
|
|
|
extensions(trackpoint);
|
2016-10-23 11:09:20 +02:00
|
|
|
else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-27 09:15:05 +02:00
|
|
|
void TCXParser::waypointData(Waypoint &waypoint)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "Position")
|
2016-10-27 09:15:05 +02:00
|
|
|
waypoint.setCoordinates(position());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "Name")
|
2016-10-27 09:15:05 +02:00
|
|
|
waypoint.setName(_reader.readElementText());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "Notes")
|
2016-10-27 09:15:05 +02:00
|
|
|
waypoint.setDescription(_reader.readElementText());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "AltitudeMeters")
|
2016-10-27 09:15:05 +02:00
|
|
|
waypoint.setElevation(number());
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "Time")
|
2016-10-27 09:15:05 +02:00
|
|
|
waypoint.setTimestamp(time());
|
2016-10-23 11:09:20 +02:00
|
|
|
else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 14:33:36 +02:00
|
|
|
void TCXParser::trackpoints(TrackData &track)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "Trackpoint") {
|
2016-10-29 10:40:30 +02:00
|
|
|
Trackpoint t;
|
|
|
|
trackpointData(t);
|
2017-06-30 18:38:16 +02:00
|
|
|
if (t.coordinates().isValid())
|
2016-10-29 10:40:30 +02:00
|
|
|
track.append(t);
|
|
|
|
else
|
|
|
|
warning("Missing Trackpoint coordinates");
|
2016-10-23 11:09:20 +02:00
|
|
|
} else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 14:33:36 +02:00
|
|
|
void TCXParser::lap(TrackData &track)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
2016-10-28 14:33:36 +02:00
|
|
|
if (_reader.name() == "Track")
|
|
|
|
trackpoints(track);
|
|
|
|
else
|
2016-10-23 11:09:20 +02:00
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:47:46 +02:00
|
|
|
void TCXParser::course(QList<Waypoint> &waypoints, TrackData &track)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
2016-10-28 14:33:36 +02:00
|
|
|
if (_reader.name() == "Track")
|
|
|
|
trackpoints(track);
|
|
|
|
else if (_reader.name() == "Name")
|
|
|
|
track.setName(_reader.readElementText());
|
|
|
|
else if (_reader.name() == "Notes")
|
|
|
|
track.setDescription(_reader.readElementText());
|
|
|
|
else if (_reader.name() == "CoursePoint") {
|
2016-10-29 10:40:30 +02:00
|
|
|
Waypoint w;
|
|
|
|
waypointData(w);
|
2017-06-30 18:38:16 +02:00
|
|
|
if (w.coordinates().isValid())
|
2017-07-27 19:47:46 +02:00
|
|
|
waypoints.append(w);
|
2016-10-29 10:40:30 +02:00
|
|
|
else
|
|
|
|
warning("Missing Trackpoint coordinates");
|
2016-10-23 11:09:20 +02:00
|
|
|
} else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 14:33:36 +02:00
|
|
|
void TCXParser::activity(TrackData &track)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "Lap")
|
2016-10-28 14:33:36 +02:00
|
|
|
lap(track);
|
2016-10-23 11:09:20 +02:00
|
|
|
else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:47:46 +02:00
|
|
|
void TCXParser::courses(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
2016-10-28 14:33:36 +02:00
|
|
|
if (_reader.name() == "Course") {
|
2017-07-27 19:47:46 +02:00
|
|
|
tracks.append(TrackData());
|
|
|
|
course(waypoints, tracks.back());
|
2016-10-28 14:33:36 +02:00
|
|
|
} else
|
2016-10-23 11:09:20 +02:00
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:47:46 +02:00
|
|
|
void TCXParser::activities(QList<TrackData> &tracks)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
2016-10-28 14:33:36 +02:00
|
|
|
if (_reader.name() == "Activity") {
|
2017-07-27 19:47:46 +02:00
|
|
|
tracks.append(TrackData());
|
|
|
|
activity(tracks.back());
|
2016-10-28 14:33:36 +02:00
|
|
|
} else
|
2016-10-23 11:09:20 +02:00
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:47:46 +02:00
|
|
|
void TCXParser::tcx(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
|
|
|
while (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "Courses")
|
2017-07-27 19:47:46 +02:00
|
|
|
courses(tracks, waypoints);
|
2016-10-23 11:09:20 +02:00
|
|
|
else if (_reader.name() == "Activities")
|
2017-07-27 19:47:46 +02:00
|
|
|
activities(tracks);
|
2016-10-23 11:09:20 +02:00
|
|
|
else
|
|
|
|
_reader.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:47:46 +02:00
|
|
|
bool TCXParser::parse(QFile *file, QList<TrackData> &tracks,
|
|
|
|
QList<RouteData> &routes, QList<Waypoint> &waypoints)
|
2016-10-23 11:09:20 +02:00
|
|
|
{
|
2017-07-27 19:47:46 +02:00
|
|
|
Q_UNUSED(routes);
|
|
|
|
|
|
|
|
_reader.clear();
|
|
|
|
_reader.setDevice(file);
|
|
|
|
|
2016-10-23 11:09:20 +02:00
|
|
|
if (_reader.readNextStartElement()) {
|
|
|
|
if (_reader.name() == "TrainingCenterDatabase")
|
2017-07-27 19:47:46 +02:00
|
|
|
tcx(tracks, waypoints);
|
2016-10-23 11:09:20 +02:00
|
|
|
else
|
2016-10-29 12:22:28 +02:00
|
|
|
_reader.raiseError("Not a TCX file");
|
2016-10-23 11:09:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return !_reader.error();
|
|
|
|
}
|