{"id":1428,"date":"2022-05-23T20:13:50","date_gmt":"2022-05-23T20:13:50","guid":{"rendered":"https:\/\/marcel-jan.eu\/datablog\/?p=1428"},"modified":"2022-05-25T08:12:54","modified_gmt":"2022-05-25T08:12:54","slug":"adding-the-track-of-my-bike-ride-on-a-folium-map","status":"publish","type":"post","link":"https:\/\/marcel-jan.eu\/datablog\/2022\/05\/23\/adding-the-track-of-my-bike-ride-on-a-folium-map\/","title":{"rendered":"Adding the track of my bike ride on a Folium map"},"content":{"rendered":"<p>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 <a href=\"https:\/\/developers.strava.com\/\">an API for developers<\/a>, but it requires connecting via OAuth 2.0 and knowledge of the API. I decided to go an easier route: because I&#8217;m Strava Premium member, I can download the GPX track of any ride, including my own.<\/p>\n<p>These .gpx track files are of the same XML structure as we saw <a href=\"https:\/\/marcel-jan.eu\/datablog\/2022\/05\/21\/digging-into-video-files-for-geolocations\/\">embedded in video files<\/a> in my last blogpost. I can just open the file and use almost the same Python code to read the locations.<\/p>\n<p><!--more--><\/p>\n<h2>Creating an AntPath<\/h2>\n<p>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.<\/p>\n<p>First we read the .gpx file and load the latitude\/longitude in a dataframe:<\/p>\n<pre>import folium\r\nimport pandas as pd\r\nimport xml.etree.ElementTree as ET\r\n\r\nride_xml_data = open(\"granfondorosa2022-epic.gpx\", 'r').read()  # Read file\r\nride_root = ET.XML(ride_xml_data)  # Parse XML<\/pre>\n<p>We prepare an empty dataframe:<\/p>\n<pre>ridedf = pd.DataFrame(columns=['latitude', 'longitude', 'elevation'])<\/pre>\n<p>And every latitude\/longitude\/elevation set is stored in a row:<\/p>\n<pre>for ride_element in ride_root.iter():\r\n    if \"trkpt\" in ride_element.tag:\r\n        ride_lat = ride_element.attrib['lat']\r\n        ride_lon = ride_element.attrib['lon']\r\n    elif \"ele\" in ride_element.tag:\r\n        ride_ele = ride_element.text\r\n    elif \"time\" in ride_element.tag:\r\n        ride_time = ride_element.text\r\n        ridedf.loc[ride_time, :] = [float(ride_lat), float(ride_lon), float(ride_ele)]<\/pre>\n<p>I&#8217;m only going to use the latitude and longitude, since I don&#8217;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.<\/p>\n<pre>loc = ridedf[['latitude', 'longitude']]<\/pre>\n<p>Next we find the center of all locations in the dataframe:<\/p>\n<pre>ride_lat_mean = ridedf['latitude'].mean()\r\nride_lon_mean = ridedf['longitude'].mean()<\/pre>\n<p>And now we create the map:<\/p>\n<pre>my_map = folium.Map(location=[ride_lat_mean, ride_lon_mean], zoom_start=12)<\/pre>\n<p>So we can create an antpath with this. We get a blue dashed line that is moving all the time:<\/p>\n<pre>plugins.AntPath(loc, color='blue', weight=10, opacity=0.8).add_to(my_map)<\/pre>\n<p>And it looks like this. But what you don&#8217;t see, is that it is animated. Really fast. And I actually don&#8217;t find it very pleasant to look at.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1429\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/05\/granfondorosa2022_ride_antpath-300x205.png\" alt=\"\" width=\"678\" height=\"463\" \/><\/p>\n<p>There are some settings you can alter, to make the animation a bit slower. I found a website where you can <a href=\"https:\/\/rubenspgcavalcante.github.io\/leaflet-ant-path\/\">play with antpath settings<\/a> and these also work quite well in Python. But whatever I changed, I liked the paused=True setting the most.<\/p>\n<p>If you don&#8217;t want an antpath Folium also has a regular, non animated polyline. And that is more to my liking. Folium&#8217;s PolyLine can use exactly the same dataframe with locations that I used for the AntPath.<\/p>\n<pre>folium.PolyLine(loc, weight=5, color='red').add_to(my_map)<\/pre>\n<p>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 <a href=\"https:\/\/www.nltourrides.nl\/gran-fondo-rosa\/\">Gran Fondo Rosa<\/a> last Sunday. And voila!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1430\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/05\/granfondorosa2022_ride_polyline-300x150.png\" alt=\"\" width=\"680\" height=\"340\" \/><\/p>\n<p>And this was me last Sunday, after this 174 km ride:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-1435\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/05\/2022GFRosa1348-1-242x300.jpg\" alt=\"\" width=\"314\" height=\"389\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Other blogposts I wrote about geo data in Python:<\/p>\n<p><a href=\"https:\/\/marcel-jan.eu\/datablog\/2022\/05\/21\/digging-into-video-files-for-geolocations\/\">Digging into video files for geolocations (Exif data in video files, running OS commands from Python, processing XML)<\/a><\/p>\n<p><a href=\"https:\/\/marcel-jan.eu\/datablog\/2022\/05\/11\/photo-locations-marker-icons-and-displaying-photos-on-my-map\/\">Photo location markers and displaying photos on a map (Accessing exif data in JPGs with Python, projecting photos on a Folium map).<\/a><\/p>\n<p><a href=\"https:\/\/marcel-jan.eu\/datablog\/2022\/05\/04\/making-my-video-location-map-even-better-with-folium\/\">Making a map of video locations in Folium<\/a><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,75],"tags":[336,319,333,335,337,76,334,316],"class_list":["post-1428","post","type-post","status-publish","format-standard","hentry","category-howto","category-python","tag-antpath","tag-folium","tag-gpx","tag-gran-fondo-rosa","tag-polyline","tag-python","tag-strava","tag-xml"],"_links":{"self":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1428","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/comments?post=1428"}],"version-history":[{"count":6,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1428\/revisions"}],"predecessor-version":[{"id":1439,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1428\/revisions\/1439"}],"wp:attachment":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/media?parent=1428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/categories?post=1428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/tags?post=1428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}