/* * 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 // for std::sort #include #include #include #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 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(); }