### Heitor's log

Yesterday my supervisor and I went to a hike to the Swiss Far West, the westernmost part of Switzerland. Unfortunately, we couldn’t go to the precise westernmost point of the border with France, because that’s in the middle of the river Le Rhône1 and we didn’t want to get inside the water.

I recorded our hike using OsmAnd, a very nice app for maps and location and finding out how to go from one place to another. And all that offline! You only need internet connection to download the app :) Oh, and it’s free!

This app gave me this GPX file. It’s basically an XML file with coordinates (latitude, longidute, elevation and time) and some more information. You can read more about the format on WikiPedia.

I found this gpxpy library to work with GPX files. It’s quite easy to use it together with matplotlib:

import gpxpy
import matplotlib.pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D

gpx_filename = "hike.gpx"

gps_lat = []
gps_long = []
gps_elevation = []

fig = plt.figure()

with open(gpx_filename, 'r') as bla:
gpx = gpxpy.parse(bla)
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
gps_lat.append(point.latitude)
gps_long.append(point.longitude)
gps_elevation.append(point.elevation)

def init():
N = len(gps_lat)
for i in range(N - 1):
ax.plot(gps_long[i:i + 2],           # x coordinate
gps_lat[i:i + 2],            # y,
gps_elevation[i:i + 2],      # z
color=plt.cm.viridis(i / N)) # sequential color

ax.set_xlabel("longitude")
ax.set_ylabel("latitude")
ax.set_zlabel("altitude (meters)")

return fig,

def animate(i):
ax.view_init(elev=10., azim=i)
return fig,

anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=360, interval=20, blit=True)
anim.save('hike.mp4', fps=30, extra_args=['-vcodec', 'libx264'])



And this codes gives the path of our hike, after about 150 seconds:

And that’s pretty cool import gpxpy

gpx_filename = "hike.gpx"
gpx = gpxpy.parse(open(gpx_filename))

print("")
print("distance:", gpx.length_3d(), "m")
print("duration:", gpx.get_duration(), "s")
print("started:", gpx.get_time_bounds())
print("ended:",   gpx.get_time_bounds())
print("elevation extremes:", gpx.get_elevation_extremes()[:], "m")
print("Max speed: ", gpx.get_moving_data()[-1], "m/s")
print("Boundaries: ", [bla for bla in gpx.get_bounds()])


\$ python infos.py

distance: 5892.264830266586 m
duration; 5550 s
started: 2018-09-24 07:36:10
ended: 2018-09-24 09:08:40
elevation extremes: (320.36, 429.36) m
Max speed:  1.8713130924592616 m/s
Boundaries:  [46.1285393, 46.13805, 5.9561619, 5.9720922]


Nice! But the nicest thing would be a simple way to add the GPX coordinates on top of the map of the region. The library geotiler provides a way to get the map from OpenStreetMap. You might need Redis running as server in the background for geotiler to work. Here’s the code:

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import matplotlib.patheffects as PathEffects
from matplotlib.patches import Ellipse
import geotiler
import gpxpy

gpx_filename = "hike.gpx"
gpx = gpxpy.parse(open(gpx_filename))

bbox = [gpx.get_bounds().min_longitude - 0.002,
gpx.get_bounds().min_latitude  - 0.002,
gpx.get_bounds().max_longitude + 0.002,
gpx.get_bounds().max_latitude  + 0.002]

fig = plt.figure(figsize=(13, 13))
ax = plt.subplot(111)

mm = geotiler.Map(extent = bbox, zoom = 16)
img = geotiler.render_map(mm)

myMap = Basemap(llcrnrlon=bbox, llcrnrlat=bbox,
urcrnrlon=bbox, urcrnrlat=bbox,
projection='merc', ax=ax)
myMap.imshow(img, interpolation='lanczos', origin='upper')

# plot hike
points = gpx.get_points_data()
lon = [p.longitude for p in points]
lat = [p.latitude  for p in points]
index = [p.point_no for p in points] # to color sequentially each point

x, y = myMap(lon, lat) # map (long, lat) to (x,y) coordinates in plot
ax.scatter(x, y, c = index, s = 4, cmap='brg')

# add some texts and arrows with style
t1 = ax.annotate('France', xy=(.01, .96), xycoords='axes fraction',
horizontalalignment='left', fontsize = 20,
verticalalignment='bottom', fontname = 'monospace')
t2 = ax.annotate('Switzerland', xy=(.99, .96), xycoords='axes fraction',
horizontalalignment='right', fontsize = 20,
verticalalignment='bottom', fontname = 'monospace')
el = Ellipse((2, -1), 0.5, 0.5)
t3 = ax.annotate('Border', xy=(.61, .90), xycoords='axes fraction',
xytext=(.5, .96), textcoords='axes fraction',
horizontalalignment='center', fontsize = 20,
verticalalignment='bottom', fontname = 'monospace',
arrowprops=dict(arrowstyle="simple",
fc="black", ec="none",
patchB=el,
t4 = ax.annotate("Switzerland's\nwestest point", xy=myMap(5.9559555, 46.1323555),
xytext=(.3, .42), textcoords='axes fraction',
horizontalalignment='center', fontsize = 20,
verticalalignment='bottom', fontname = 'monospace',
arrowprops=dict(arrowstyle="simple",
fc="black", ec="none",
patchB=el, 