Adding the track of my bike ride on a Folium map

Having markers of videos and photos taken during my bike ride is cool and all, but how about having a track of the bike ride itself? All my bike rides are registered on Strava, the cycling and running app. Strava has an API for developers, but it requires connecting via OAuth 2.0 and knowledge of the API. I decided to go an easier route: because I’m Strava Premium member, I can download the GPX track of any ride, including my own.

These .gpx track files are of the same XML structure as we saw embedded in video files in my last blogpost. I can just open the file and use almost the same Python code to read the locations.

Creating an AntPath

All we need to do is plot a track. I heard one way to do it in Folium, is using an AntPath. So I tried that first.

First we read the .gpx file and load the latitude/longitude in a dataframe:

import folium
import pandas as pd
import xml.etree.ElementTree as ET

ride_xml_data = open("granfondorosa2022-epic.gpx", 'r').read()  # Read file
ride_root = ET.XML(ride_xml_data)  # Parse XML

We prepare an empty dataframe:

ridedf = pd.DataFrame(columns=['latitude', 'longitude', 'elevation'])

And every latitude/longitude/elevation set is stored in a row:

for ride_element in ride_root.iter():
    if "trkpt" in ride_element.tag:
        ride_lat = ride_element.attrib['lat']
        ride_lon = ride_element.attrib['lon']
    elif "ele" in ride_element.tag:
        ride_ele = ride_element.text
    elif "time" in ride_element.tag:
        ride_time = ride_element.text
        ridedf.loc[ride_time, :] = [float(ride_lat), float(ride_lon), float(ride_ele)]

I’m only going to use the latitude and longitude, since I don’t think elevation has any thing to add on the map. For points of the line Folium needs a list of (latitude/longitude) lists. But a dataframe with only the latitude and longitude pairs works just fine.

loc = ridedf[['latitude', 'longitude']]

Next we find the center of all locations in the dataframe:

ride_lat_mean = ridedf['latitude'].mean()
ride_lon_mean = ridedf['longitude'].mean()

And now we create the map:

my_map = folium.Map(location=[ride_lat_mean, ride_lon_mean], zoom_start=12)

So we can create an antpath with this. We get a blue dashed line that is moving all the time:

plugins.AntPath(loc, color='blue', weight=10, opacity=0.8).add_to(my_map)

And it looks like this. But what you don’t see, is that it is animated. Really fast. And I actually don’t find it very pleasant to look at.

There are some settings you can alter, to make the animation a bit slower. I found a website where you can play with antpath settings and these also work quite well in Python. But whatever I changed, I liked the paused=True setting the most.

If you don’t want an antpath Folium also has a regular, non animated polyline. And that is more to my liking. Folium’s PolyLine can use exactly the same dataframe with locations that I used for the AntPath.

folium.PolyLine(loc, weight=5, color='red').add_to(my_map)

So I combined this in my code that produces the video and photo locations. I ran it against my fresh new collection of videos and photos I took at the Gran Fondo Rosa last Sunday. And voila!

And this was me last Sunday, after this 174 km ride:


Other blogposts I wrote about geo data in Python:

Digging into video files for geolocations (Exif data in video files, running OS commands from Python, processing XML)

Photo location markers and displaying photos on a map (Accessing exif data in JPGs with Python, projecting photos on a Folium map).

Making a map of video locations in Folium


This entry was posted in Howto, Python and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *