Files
GoldenCheetah/src/GcRideFile.cpp

186 lines
6.8 KiB
C++

/*
* Copyright (c) 2009 Sean C. Rhea (srhea@srhea.net),
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "GcRideFile.h"
#include <algorithm> // for std::sort
#include <QDomDocument>
#include <QVector>
#include <assert.h>
#define DATETIME_FORMAT "yyyy/MM/dd hh:mm:ss' UTC'"
static int gcFileReaderRegistered =
RideFileFactory::instance().registerReader(
"gc", "GoldenCheetah Native Format", new GcFileReader());
RideFile *
GcFileReader::openRideFile(QFile &file, QStringList &errors) const
{
QDomDocument doc("GoldenCheetah");
if (!file.open(QIODevice::ReadOnly)) {
errors << "Could not open file.";
return NULL;
}
bool parsed = doc.setContent(&file);
file.close();
if (!parsed) {
errors << "Could not parse file.";
return NULL;
}
RideFile *rideFile = new RideFile();
QDomElement root = doc.documentElement();
QDomNode attributes = root.firstChildElement("attributes");
for (QDomElement attr = attributes.firstChildElement("attribute");
!attr.isNull(); attr = attr.nextSiblingElement("attribute")) {
QString key = attr.attribute("key");
QString value = attr.attribute("value");
if (key == "Device type")
rideFile->setDeviceType(value);
if (key == "Start time")
rideFile->setStartTime(QDateTime::fromString(value, DATETIME_FORMAT).toLocalTime());
}
QVector<double> intervalStops; // used to set the interval number for each point
RideFileInterval add; // used to add each named interval to RideFile
QDomNode intervals = root.firstChildElement("intervals");
if (!intervals.isNull()) {
for (QDomElement interval = intervals.firstChildElement("interval");
!interval.isNull(); interval = interval.nextSiblingElement("interval")) {
// record the stops for old-style datapoint interval numbering
double stop = interval.attribute("stop").toDouble();
intervalStops.append(stop);
// add a new interval to the new-style interval ranges
add.stop = stop;
add.start = interval.attribute("start").toDouble();
add.name = interval.attribute("name");
rideFile->addInterval(add.start, add.stop, add.name);
}
}
std::sort(intervalStops.begin(), intervalStops.end()); // just in case
int interval = 0;
QDomElement samples = root.firstChildElement("samples");
if (samples.isNull()) {
errors << "no sample section in ride file";
return NULL;
}
bool recIntSet = false;
for (QDomElement sample = samples.firstChildElement("sample");
!sample.isNull(); sample = sample.nextSiblingElement("sample")) {
double secs, cad, hr, km, kph, nm, watts, alt, lon, lat;
secs = sample.attribute("secs", "0.0").toDouble();
cad = sample.attribute("cad", "0.0").toDouble();
hr = sample.attribute("hr", "0.0").toDouble();
km = sample.attribute("km", "0.0").toDouble();
kph = sample.attribute("kph", "0.0").toDouble();
nm = sample.attribute("nm", "0.0").toDouble();
watts = sample.attribute("watts", "0.0").toDouble();
alt = sample.attribute("alt", "0.0").toDouble();
lon = sample.attribute("lon", "0.0").toDouble();
lat = sample.attribute("lat", "0.0").toDouble();
while ((interval < intervalStops.size()) && (secs >= intervalStops[interval]))
++interval;
rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, interval);
if (!recIntSet) {
rideFile->setRecIntSecs(sample.attribute("len").toDouble());
recIntSet = true;
}
}
if (!recIntSet) {
errors << "no samples in ride file";
return NULL;
}
return rideFile;
}
#define add_sample(name) \
if (present->name) \
sample.setAttribute(#name, QString("%1").arg(point->name));
void
GcFileReader::writeRideFile(const RideFile *ride, QFile &file) const
{
QDomDocument doc("GoldenCheetah");
QDomElement root = doc.createElement("ride");
doc.appendChild(root);
QDomElement attributes = doc.createElement("attributes");
root.appendChild(attributes);
QDomElement attribute = doc.createElement("attribute");
attributes.appendChild(attribute);
attribute.setAttribute("key", "Start time");
attribute.setAttribute(
"value", ride->startTime().toUTC().toString(DATETIME_FORMAT));
attribute = doc.createElement("attribute");
attributes.appendChild(attribute);
attribute.setAttribute("key", "Device type");
attribute.setAttribute("value", ride->deviceType());
if (!ride->intervals().empty()) {
QDomElement intervals = doc.createElement("intervals");
root.appendChild(intervals);
foreach (RideFileInterval i, ride->intervals()) {
QDomElement interval = doc.createElement("interval");
intervals.appendChild(interval);
interval.setAttribute("name", i.name);
interval.setAttribute("start", QString("%1").arg(i.start));
interval.setAttribute("stop", QString("%1").arg(i.stop));
}
}
if (!ride->dataPoints().empty()) {
QDomElement samples = doc.createElement("samples");
root.appendChild(samples);
const RideFileDataPresent *present = ride->areDataPresent();
assert(present->secs);
foreach (const RideFilePoint *point, ride->dataPoints()) {
QDomElement sample = doc.createElement("sample");
samples.appendChild(sample);
assert(present->secs);
add_sample(secs);
add_sample(cad);
add_sample(hr);
add_sample(km);
add_sample(kph);
add_sample(nm);
add_sample(watts);
add_sample(alt);
add_sample(lon);
add_sample(lat);
sample.setAttribute("len", QString("%1").arg(ride->recIntSecs()));
}
}
QByteArray xml = doc.toByteArray(4);
if (!file.open(QIODevice::WriteOnly))
assert(false);
if (file.write(xml) != xml.size())
assert(false);
file.close();
}