map-runs: overlaying Runkeeper runs on a single map
A simple python tool for combining GPS files in HTML
Background
I always use Runkeeper to track my runs. Its free version provides just about everything I need for following my running progress: charts for pace, elevation, cadence… and most importantly, it tracks my GPS position and renders it in a nice map:
Although I really enjoy these functionalities, one thing that I always wanted to see was a single world map with all runs combined. This need started after I played the first DLC of The Legend of Zelda: Breath of the Wild. In it, I was introduced to an awesome feature: “Hero’s Path Mode”, which kept track of my position in Hyrule’s map for the last 200 hours of gameplay:
This feature was HUGE, not necessarily for knowing where I have been, but where I haven’t been, so that I could explore those never before seen areas (in hope of shrines or korok seeds). So then I thought back about the real world, and remembered Änggårdsbergen, a big nature reserve in Gothenburg (my hometown at the time), where even though I had been many times running, I knew that it had several sections I had not explored yet. Wouldn’t be convenient that my Runkeeper app then showed me the overlay of all of my runs into a single map, so that I could find nice new areas with undiscovered ponds, peaks and views? And as in May 2019 Runkeeper did not provide that feature and I was unemployed with lots of free time, I decided to create it myself.
Implementation
The solution, using Python, was pretty simple.
Here I downloaded each run I have ever gone for as a .gpx
file, with my GPS location recorded every ~5 seconds. The very handy
gpxpy package let me then parse these files into lists of longitude/latitude pairs:
import gpxpy
gpx_file = open(<run_file.gpx>, 'r')
gpx = gpxpy.parse(gpx_file)
run_list = list()
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
run_list.append([point.longitude, point.latitude])
Then, using the folium package, which in turn calls the Leaflet.js library for interactive maps, I easily created a map object:
import folium
run_map = folium.Map(location=[<lat>, <lon>]) # lat/lon where I run most often
With both the run lists and the run map, I added each list to the map as a GeoJson
object (as other object types could not render after adding around 70 to the same map):
geojson = folium.GeoJson({'type': 'LineString', 'coordinates': run_list})
geojson.add_to(run_map)
The final step was to export the object to an .html
map:
run_map.save('./output-map.html')
That’s it! I added some additional display settings, such as transparency to show overlapped runs, highlighting of any run you hover on, and a tooltip to display the date + type of activity (sometimes I use Runkeeper for tracking other activities such as hiking or biking).
End product
The map for Änggårdsbergen below:
Knowing the areas I had not been to, I was then able to plan pre-run where would I go each time. As an added bonus, by zooming out enough I also got for free a world map with all places I’ve been running:
However, the final challenge was that as new run logs would come in, I needed a way to keep the map updated. I’m a sucker for automation, so I added all .gpx
files + scripts to
this Github repo, and I set up
Travis CI to update the map every time I add new runs. Now the only thing I need to do every 3 months (when my
Trello board reminds me) is to download all new .gpx
files to the proper folder and commit + push the files to Github. Travis will then run the script to create the latest map and publish it
here.
Final Words
Could this tool be better? For sure. It could display extra information like total distance or some basic stats, but those are features that Runkeeper already provides. Additionally, the tool could be packaged as a full fledged app that also shows you your current position, to use in real time when exploring new areas. Alas, what I have for now is good enough for my purpose.
Interested in any of this? I documented here the instructions on how to implement it for your own runs. Feel free to comment below if you find the tool useful or if you have any feedback!
Ben
PS: Thanks to @eHanseJoerg for this useful jupyter notebook explaining how to use the geoJSON format in folium.