exports nike+ runs to gpx
<pre> | |
strt importing gpx files ... | |
<?php | |
require_once 'lib/init.php'; // init $n stuff (login) | |
$dir = "import"; | |
$runs = $n->activities(); | |
foreach($runs->activities as $a) { | |
$runId = $a->activityId; | |
$fileName = sprintf('%s/%s.gpx', $dir, $runId); | |
if(!file_exists($fileName)) { | |
echo "importing run " . $fileName . "\n"; | |
$run = $n->run($runId); | |
if($run === NULL) { | |
echo "cannot import " . $runId . "\n"; | |
continue; | |
} | |
file_put_contents($fileName, $n->toGpx($run)); | |
} | |
} | |
?> | |
eof |
<? | |
class NikePlusPHPGpxExport extends NikePlusPHP { | |
/** | |
* toGpx() | |
* outputs a run object to a runtastic importable gpx document | |
* | |
* @param object $run output from run() | |
* | |
* @return string gpx string | |
*/ | |
public function toGpx($run) { | |
$activity = $run->activity; | |
$waypoints = $run->activity->geo->waypoints; | |
$startTime = strtotime($run->activity->startTimeUtc); | |
$name = 'Nike+ ' . $run->activity->name; | |
$description = $run->activity->tags->note; | |
$heartRate = null; | |
$distance = null; | |
foreach($run->activity->history as $hi) { | |
if($hi->type == 'HEARTRATE') $heartRate = $hi; | |
if($hi->type == 'DISTANCE') $distance = $hi; | |
} | |
$b = new XMLWriter(); | |
$b->openMemory(); | |
$b->setIndent(true); | |
$b->setIndentString(" "); | |
$b->startDocument("1.0", "UTF-8"); | |
$b->startElement('gpx'); | |
$b->writeAttribute('version', '0.1'); | |
$b->writeAttribute('creator', 'nikeplusphp'); | |
$b->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); | |
$b->writeAttribute('xmlns', 'http://www.topografix.com/GPX/1/1'); | |
$b->writeAttribute('xsi:schemaLocation', 'http://www.topografix.com/GPX/1/1 http://www.topografix.com/gpx/1/1/gpx.xsd'); | |
$b->writeAttribute('xmlns:gpxtpx', 'http://www.garmin.com/xmlschemas/TrackPointExtension/v1'); | |
$b->writeAttribute('xmlns:gpxx', 'http://www.garmin.com/xmlschemas/GpxExtensions/v3'); | |
// metadata | |
$b->startElement('metadata'); | |
$b->writeElement('name', $name); | |
$b->writeElement('desc', $description); | |
// get min/max lat/lng | |
$minLon = 10000; | |
$maxLon = -10000; | |
$minLat = 10000; | |
$maxLat = -10000; | |
foreach($waypoints as $wp) { | |
if($wp->lon > $maxLon) $maxLon = $wp->lon; | |
if($wp->lon < $minLon) $minLon = $wp->lon; | |
if($wp->lat > $maxLat) $maxLat = $wp->lat; | |
if($wp->lat < $minLat) $minLat = $wp->lat; | |
} | |
$b->startElement('bounds'); | |
$b->writeAttribute('maxlon', $maxLon); | |
$b->writeAttribute('minlon', $minLon); | |
$b->writeAttribute('maxlat', $maxLat); | |
$b->writeAttribute('minlat', $minLat); | |
$b->endElement(); // EO bounds | |
$b->endElement(); // EO metadata | |
// track | |
$b->startElement('trk'); | |
$b->writeElement('name', 'trkName ' . time()); | |
$b->writeElement('type', 'Run'); | |
$b->startElement('trkseg'); | |
foreach($waypoints as $index => $wp) { | |
$b->startElement('trkpt'); | |
$b->writeAttribute('lat', $wp->lat); | |
$b->writeAttribute('lon', $wp->lon); | |
$b->writeElement('ele', $wp->ele); | |
$b->writeElement('time', date('Y-m-d\TH:i:s', $startTime+$index)); | |
$b->startElement('extensions'); | |
$b->startElement('gpxtpx:TrackPointExtension'); | |
if($heartRate !== null) { | |
$hrKey = (int) floor($index/$heartRate->intervalMetric); | |
if(array_key_exists($hrKey, $heartRate->values)) { | |
$b->writeElement('gpxtpx:hr', $heartRate->values[$hrKey]); | |
} | |
} | |
$b->endElement(); // EO gpxtpx:TrackPointExtension | |
$b->endElement(); // EO extensions | |
$b->endElement(); // EO trkpt | |
} | |
$b->endElement(); // EO trkseg | |
$b->endElement(); // EO trk | |
$b->endElement(); // EO gpx | |
$b->endDocument(); | |
return $b->outputMemory(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
I only found this issue after I imported a GPX file into another running app and was told I'd managed to run at over 17 miles per hour. I don't know if this is the result of a change by Nike or if it's always been an issue. $index taken from the way points array is not a duration reference, so the times being set for each way point in the GPX are wrong. For whatever reason the Nike output does not include a time or duration value with their way point data. They do have separate KM and Mile split arrays which include a duration for each point, but then you lose a load of way points and I imagine the map would be messed up. The only solution I could come up with was to create an even increment based on the duration and number of way points.
$incr = ($duration/1000)/count($waypoints);
foreach($waypoints as $index => $wp) {
...
if ($index == 0) {
$b->writeElement('time', date('Y-m-d\TH:i:s', $startTime));
} else if ($index == (count($waypoints) - 1)) {
$b->writeElement('time', date('Y-m-d\TH:i:s', $startTime+($duration/1000)));
} else {
$b->writeElement('time', date('Y-m-d\TH:i:s', $startTime+($incr*$index)));
}
...
}