# Tour du Mont-Blanc 2016 Analysis¶

### By Jeremy Tuloup - @jtpio¶

Facing new challenges is always a good thing. And they can be of any type.

In August 2016, I decided to go hiking around the Mont-Blanc (highest montain in Europe).

9 days. 3 people. Carrying a tent and using it every day. ~160 km in total.

I had several goals in mind:

1. For personal reasons, this is something I had wanted to do for about a year
2. Record GPS data and write some kind of report with numbers and graphs
3. Face a new challenge to push my physical limits and motivation further

While I was preparing the Tour du Mont-Blanc (commonly called TMB), it was quite difficult to find good information about the different days. There are plenty of blog posts from other trekkers, books and guides with useful advice. That's true. But what I really wanted was a good online resource with the number of kilometers as well as an estimation of the elevation and times for all the different parts of the track. And I couln't find any!

The purpose of this notebook is to provide this "stats oriented guide", and show how convenient it can be to mix programming and data to tell a story.

## Gear¶

The GPS data was recorded with a standard Android phone (Nexus 5).

Every morning, we would simply start the recording, let the device do its job and stop the recording at the end of the day. Sometimes (although not systemactically), I would place a mark to indicate longer breaks (e.g lunch).

Looking at the graphs below, you will notice disparities in the data, caused by the quality of the sampling. They can be quite annoying, especially when computing summary statistics related to the elevation. But data analysis is also about this: dealing with data that is not necessary clean and well formated.

## Visualizing the data¶

There are several websites that make it possible to visualize GPS data.

A good one is mapexplorer.com. Upload a GPX file to visualize both the trace map and the elevation graph. Quite useful for single tracks, but not very practical in our case when the goal is to combine several days on the same page.

Thankfully, the Jupyter Notebook makes it quite easy to write text and show photos while plotting data. Super convenient to tell a story!

## Setup¶

This analysis was based on a fresh Anaconda environment.

In [1]:
import sys
print(sys.version)

3.5.2 |Continuum Analytics, Inc.| (default, Jul  2 2016, 17:53:06)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]


Let's define some tools and libraries needed to create the statistics and graphs. We rely on some useful packages:

pip install seaborn folium gpxpy piexif srtm.py

In [2]:
import datetime
import folium
import glob
import gpxpy
import matplotlib.pyplot as plt
import os
import pandas as pd
import piexif
import seaborn as sns
import srtm
from collections import defaultdict
from IPython.display import display, HTML
from pytz import timezone
from statistics import mean

%matplotlib inline
plt.rcdefaults()
sns.set_style('darkgrid')
sns.set_palette('deep', desat=.6)
sns.set_context(rc={"figure.figsize": (16, 9)})

# tweak table display
display(HTML('<style>.rendered_html td, .rendered_html th { text-align: center; } table { width: 100%;} </style>'))

france = timezone('Europe/Paris')


folium will display the maps, and gpxpy load and manipulate gpx data.

srtm will help fix the elevation data recorded by the Android device. The raw data is indeed not that good and shows lots of jumps in the elevation.

In [3]:
DATA_FOLDER = './gps_data/'
data_files = [os.path.join(DATA_FOLDER, f) for f in os.listdir(DATA_FOLDER)]

# sort by day
data_files.sort()

# retrieve the elevation data to fix the raw (recorded) elevation
elevation_data = srtm.get_data()


Now, let's build a mini framework that will be reused for each day. Doing so, we can minimize the number of lines of code and focus on the story telling.

To make it simpler, let's load all the data at once.

In [4]:
day_points = []
stats = defaultdict(list)

for file in data_files:
with open(file, 'r') as f:
gpx = gpxpy.parse(f)

points = gpx.get_points_data()
day_points.append({
'points': points,
'waypoints': gpx.waypoints
})

lowest, highest = gpx.get_elevation_extremes()
uphill, downhill = gpx.get_uphill_downhill()
stats['Date'].append(gpx.get_time_bounds().start_time)
stats['Distance'].append(round(points[-1].distance_from_start / 1000, 2))
stats['Duration'].append(str(datetime.timedelta(seconds=gpx.get_duration())))
stats['Lowest'].append(int(lowest))
stats['Highest'].append(int(highest))
stats['Uphill'].append(int(uphill))
stats['Downhill'].append(int(downhill))

df = pd.DataFrame(
stats,
columns=['Date', 'Distance', 'Duration', 'Lowest', 'Highest', 'Uphill', 'Downhill']
)

df.columns = ['Date', 'Distance (km)', 'Duration', 'Lowest (m)', 'Highest (m)', 'Uphill (m)', 'Downhill (m)']
# reindex by date
df['Date'] = df['Date'].map(lambda dt: dt.replace(tzinfo=france))


There is another interesting thing to do with an interactive map: place markers along the trail and show a photo taken at that position (similar to the way Google Maps does it).

This requires having the GPS coordinates for each photo. While most of the smartphones add them automatically in the metadata, this is not the case for my old camera :(

However, the camera saves the time at which the photo was taken. To associate a photo with a location, we can go through the list of location and compare the time.

In [5]:
# offset between the camera and the phone (still winter time)
CLOCK_OFFSET = datetime.timedelta(hours=2, minutes=0)

In [6]:
# Serve the images from the web so they are correctly displayed in the folium iframe
BASE_PHOTO_URL = 'https://raw.githubusercontent.com/jtpio/data-playground/master/tmb/'

In [7]:
def plot_photos(m, points, day):
for img_file in glob.iglob('./photos/{}/*.JPG'.format(day), recursive=True):
raw_date = exif_dict['0th'][piexif.ImageIFD.DateTime].decode('utf-8')
d = datetime.datetime.strptime(raw_date, '%Y:%m:%d %H:%M:%S') - CLOCK_OFFSET
closest_point = min(points, key=lambda p: abs(p.point.time - d)).point

# normalize the pictures size
width = 600
resolution_x = exif_dict['Exif'][piexif.ExifIFD.PixelXDimension]
resolution_y = exif_dict['Exif'][piexif.ExifIFD.PixelYDimension]
ratio = resolution_x / resolution_y
height = width / ratio

lat, lng = closest_point.latitude, closest_point.longitude
img_link = os.path.join(BASE_PHOTO_URL, img_file)
img_html = '<img src="{}" width={}/>'.format(img_link, width)
img_frame = folium.element.IFrame(html=img_html, width=width + 20, height=height + 20)
popup = folium.Popup(img_frame, max_width=width + 20)
marker = folium.Marker((lat, lng), popup=popup,
icon=folium.Icon(color='green', icon='picture'))


Then, given a set of points, plot them on a map.

In [8]:
def plot_track(m, points, waypoints, zoom):
points = [p.point for p in points]
mean_lat = mean(p.latitude for p in points)
mean_lng = mean(p.longitude for p in points)

# create the map
m.location = [mean_lat, mean_lng]
m.zoom_start = zoom

pts = [(p.latitude, p.longitude) for p in points]
folium.PolyLine(pts, color='red', weight=2.5, opacity=1).add_to(m)


Given a set of points, plot the elevation over the distance.

In [9]:
def plot_elevation(points, waypoints):
px = [p.distance_from_start / 1000 for p in points]
py = [p.point.elevation for p in points]
plt.plot(px, py)
plt.xlabel('Distance (km)')
plt.ylabel('Elevation (m)')
plt.xlim(0, px[-1])
plt.show()


Given a day, show its statistics.

In [10]:
def plot_day(day, zoom=13):
m = folium.Map()
points = day_points[day - 1]['points']
wps = day_points[day - 1]['waypoints']

display(HTML(df[day-1:day].to_html(index=False)))
plot_track(m, points, wps, zoom)
plot_photos(m, points, day)
display(m)

plot_elevation(points, wps)


Plot all the days.

In [11]:
def plot_all_days(zoom=13):
points = [pt for pts in day_points for pt in pts['points']]
m = folium.Map()
plot_track(m, points, [], zoom)
display(m)


All set. Time for the trip!

## Day 1: Les Houches - Chalets de Miage¶

In [12]:
plot_day(1)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-01 08:11:29+02:00 15.29 5:53:51 988 2114 1356 776

Quite challenging for a first day!

We started with the rain. Damn, that wasn't really convincing. Good for a first motivation test though. Fortunately the weather became way better a few hours later and sunny the entire afternoon.

Pretty steep start, going huphill until Col de la Voza. We continued our way up to reach the Col du Tricot, followed by a quite abrupt way down to Chalets de Miage.

## Day 2: Chalets de Miage - Refuge du Col de la Croix du Bonhomme¶

In [13]:
plot_day(2, zoom=12)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-02 06:50:55+02:00 20.03 8:19:05 1146 2485 1567 683

The second day was probably one of the most difficult of the entire trip:

• It was the second day only, still a long way to go until the end
• First pain in the back and on the shoulders due to the heavy backpacks (even though it was quite expected)
• Long distance and high elevation difference

In the morning, we passed though the small village of Les Contamines-Montjoie. Perfect timing to pick up fresh fruits at the market.

It was also a good opportunity to have a look at the elegant Notre Dame de la Gorge church.

Arriving at Refuge du Col de la Croix du Bonhomme (loooooong name), we were pleased to find a herd of mountain goats on the creast. Since it was the end of the day, it gave a nice silhouette effect.

## Day 3: Refuge du Col de la Croix du Bonhomme - Lac du Miage¶

In [14]:
plot_day(3, zoom=12)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-03 06:48:53+02:00 21.38 12:00:35 1783 2662 1085 1511

Day 3 was another long day, full of surprises with its variety of landscapes. The valley from Col de la Seigne to Lac du Miage was quite daunting: such a big opening in the middle of the mountains.

At first, we thought we could find a nice spot for the night around the Lac de Combal. It turns out it's more a swamp than a lake!

We pushed the hike a little bit further to Lac du Miage to find a very sweet camping spot. We could also take a (refreshing) bath in this lake, quick but very appreciated.

## Day 4: Lac du Miage - Camping "La Sorgente" (Courmayeur)¶

In [15]:
plot_day(4)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-04 08:15:36+02:00 15.39 9:45:39 1207 1999 177 959

A day with less than 200 m uphill. How unusual this is!

Following the two previous demanding days, we decided it would be wise to take it easy for day 4.

We first hiked down to a proper camp site, Camping "La Sorgente". Comfort and shower helped us recharge batteries. We then decided to walk down to Courmayeur and visit the city. At the end of the day, we went back to the camp site by bus before the storm.

## Day 5: Courmayeur - Chalet Val Ferret¶

In [16]:
plot_day(5, zoom=12)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-05 08:13:52+02:00 17.38 8:05:56 1262 2043 1285 793

Our first rainy day :(

We took the bus down from Camping "La Sorgente" to Courmayeur, since we already walked this part the day before.

We knew we would have at least a day of rain during the entire trip. Even though the elevation chart looks quite flat, the rain made the paths very slippery and muddy. Full focus required, especially for the way down at the end of the day.

We stopped next to the Chalet Val Ferret, and spent the evening eating warm food and play cards. Our first goal was to reach the Refuge Elena, but in retrospect it was so much wiser to stop a bit before (Refuge Elena is very exposed to the wind).

## Day 6: Chalet Val Ferret - Champex-Lac¶

In [17]:
plot_day(6)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-06 08:22:57+02:00 14.99 6:38:24 1440 2534 683 1018

Another day, another country.

Time to go to Switzerland. We first passed next to Refuge Elena, then the Grand Col Ferret. It was super windy up there.

We made our way down to La Fleury and took the bus directly to Champex-Lac (notice the vertical drop on the graph, the recording was stopped).

Why the bus? This is something we had planned beforehand to be able to do the tour in 9 days. It saved us many kilometers and time. Plus, this portion of the trip looked a bit less interesting (as mentioned in other blog posts).

## Day 7: Champex-Lac - Col de la Forclaz¶

In [18]:
plot_day(7)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-07 07:33:03+02:00 15.18 8:34:34 1492 2557 1411 1371

Starting from Champex-Lac, we could choose to go either with the normal route, or with a variant.

We went for the variant (more challenge = more fun!), and were not disappointed at all.

This brought us to Fenêtre d'Arpette, one of the highest points of the Tour du Mont Blanc. The weather was beautiful and the view from the top amazing. Totally worth it.

The way down to Col de la Forclaz was however rather long and repetitive. Bad for the knees once again!

## Day 8: Col de la Forclaz - Argentière¶

In [19]:
plot_day(8, zoom=12)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-08 07:59:23+02:00 18.04 7:39:47 1219 2198 1193 1257

As we were approaching the end of the trip, we were more and more customizing the trip depending on our needs and physical conditions.

We really wanted to have a look at the Lac Blanc the day after, but it wasn't a realistic goal unfortunately. Plus we learned that the weather would be quite unpleasant (rain), which made us stop in Argentiere and skip the Lac Blanc.

The elevation graph shows a few vertical lines, due to GPS signal loss.

## Day 9: Argentière - Chamonix¶

In [20]:
plot_day(9)

Date Distance (km) Duration Lowest (m) Highest (m) Uphill (m) Downhill (m)
2016-08-09 08:19:04+02:00 13.98 6:16:44 1046 1876 1048 1223