I set a goal to run at least 1 mile every day in April. Most days I ran from my home around the neighborhood, usually taking a different route. I love maps and I was interested in tracking my routes, so I logged each run on an Apple Watch, downloaded the data, and plotted the routes with p5.js. Here are the results:

If you’re interested in doing the same, here is the process I used:

  1. Get GPS data from iPhone
  2. Read GPS data from files with JavaScript
  3. Sketch the runs on an HTML canvas with p5.js

Step 1 - Get GPS Data From iPhone

GPS data is stored on the iPhone from logged Apple Watch workouts. It can be be exported from the iPhone by following these steps:

  1. Open the Health App
  2. Tap on your icon in the top right
  3. scroll to the bottom of the page
  4. Tap “Export All Health Data”
  5. Tap “Export” and select how you want to send it to yourself (I saved to Dropbox to access it on my computer)

I expect other devices with GPS tracking have similar capabilities and export capabilities. What you want is the GPX files.

Step 2 - Read GPX Files with JS

Disclaimer: I’ve never done this before and I’m not a front-end developer. I have limited JavaScript and HTML experience. I struggled through this process and made it work well enough, but only just. There will be better ways to do this. I got it done, and moved on.

After I got the GPX files onto my computer, I had to figure out a way to read them. Here is what a GPX file looks like from an example run on April 6th. Location information (latitude and logitude) has been de-identified (location data X’d out).

<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Apple Health Export" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
  <metadata>
    <time>2021-04-06T19:58:17Z</time>
  </metadata>
  <trk>
    <name>Route 2021-04-01 11:47am</name>
    <trkseg>
      <trkpt lon="-XX.XXXXX" lat="XX.XXXXX"><ele>XX.XXXXX</ele><time>2021-04-03T19:20:59Z</time><extensions><speed>2.302255</speed><course>342.171021</course><hAcc>3.531085</hAcc><vAcc>2.115055</vAcc></extensions></trkpt>
      <trkpt lon="-XX.XXXXX" lat="XX.XXXXX"><ele>XX.XXXXX</ele><time>2021-04-03T19:21:00Z</time><extensions><speed>2.378216</speed><course>320.507385</course><hAcc>2.533836</hAcc><vAcc>1.800768</vAcc></extensions></trkpt>
      ...
      <trkpt lon="-XX.XXXXX" lat="XX.XXXXX"><ele>XX.XXXXX</ele><time>2021-04-03T19:37:57Z</time><extensions><speed>2.221309</speed><course>340.289551</course><hAcc>2.731670</hAcc><vAcc>1.350663</vAcc></extensions></trkpt>
    </trkseg>
  </trk>
</gpx>

It has general info (what kind of data file it is, what created it, when it was created), and then it contains all of the track points (trk). These points have several pieces of information - I’m just interested in the position (longitude and latitude) and speed.

I had to figure out a way to read this data into JavaScript and save it as an array so that I can plot in on a canvas. So I did what I always did, and Googled until I found something resembling a solution. Here is what I found and modified for my needs. I created a JavaScript function for reading a GPX file, parsing the XML within, and returning the latitude, longitude, and speed as an array. Here’s what that looks like:

function readGPX(file) {    
  let latp = [];
  let lonp = [];
  let speedp = [];
  fetch(file)
    .then(response => response.text())
    .then(str => (new window.DOMParser()).parseFromString(str, "text/xml"))
    .then(doc => {
      const nodes = [...doc.getElementsByTagName('trkpt')];
      nodes.forEach(node =>
      {
        latp.push(node.getAttribute("lat"));
        lonp.push(node.getAttribute("lon"));
      })
      const speeds = doc.getElementsByTagName('speed');
      for (let i= 0; i < speeds.length; i++) {
        speedp.push(speeds[i].innerHTML);
      }
    })
    return [latp,lonp,speedp]
}

The first time I tried to run my index.html file in a browser, I got this error: “Fetch API cannot load ‘file’. URL scheme must be “http” or “https” for CORS request.” After yet more Googling, I figured out that you have to be running a live server. I installed and used the Live Server VSCode extension to get around this problem. Just install the extension, push the ‘Go Live’ button on the bottom right info bar, right click the index.html file, and select ‘Open with Live Sever’. No more errors.

Several routes can be loaded at once and stored into an array. To do that, I first generate a list of the files. One way to do this is to navigate to the directory where the files are stored and enter this into the command line: dir /b read *.gpx . This creates a list of only the gpx files, and only the names of the file. There is probably a more elegant way to do this by letting JavaScript read the names of the files in a directory, but I wasn’t sure how to do that. Using the list of files, the function for reading GPX data (readGPX()) can be run for each and the data can be stored into arrays:

let latRoutes = [];
let lonRoutes = [];
let speedRoutes =[];
let files = ['route_2021-04-01_11.47am.gpx',
'route_2021-04-02_8.38am.gpx',
...
'route_2021-04-30_7.53am.gpx'];

// preload the data from the gpx files
function preload(){
    // get the latitude, longitutde, and speed from the .gpx files
    for (let i=0;i<files.length;i++){
        [latRoutes[i], lonRoutes[i], speedRoutes[i]] = readGPX(files[i]);
    }
}

Step 3 - Draw the Routes

Now that the lattitude, longitute, and speed have been saved into arrays, the data can be displayed anyway you want. Here’s where you can get creative.

function preload() {
    for (let i=0;i<files.length;i++){
        [latRoutes[i], lonRoutes[i], speedRoutes[i]] = readGPX(files[i]);
    }
}
function setup() {
    createCanvas(750,750);
}
function draw() {
    // sketch!
}

First I plotted black lines over a white background (seen above). I also did a sketch with linearly interpolated colors from red to green based on speed. Red = walking, green = running. Here’s what that looked like. You can see how many times I had to walk to make it more than a mile - I never said I was a good runner…

Going further

  • I’m sure the GPX data files could be parsed and handled better, like I said, I’m not an expert and I was in a get-it-done mindset. One source I found after the fact is GPXParser.js that looked like it may have been helpful.
  • There are a lot of tools already out there, online or downloadable, that allow you to track and plot your GPS routes. I know you can do it with the desktop version of Google Earth.
  • There may be interesting ways to include maps in the background by using leaflet, mappa, or other tools.