Where do you run to? Map your Strava activities on static and Leaflet maps.

By Sascha W.

(This article was first published on Rcrastinate, and kindly contributed to R-bloggers)

So, Strava’s heatmap made quite a stir the last few weeks. I decided to give it a try myself. I wanted to create some kind of “personal heatmap” of my runs, using Strava’s API. Also, combining the data with Leaflet maps allows us to make use of the beautiful map tiles supported by Leaflet and to zoom and move the maps around – with the runs on it, of course.

So, let’s get started. First, you will need an access token for Strava’s API. I found all the necessary information for this in this helpful “Getting started” post. As soon as you have the token, you have access to your own data.

Now, let’s load some packages and define functions for getting and handling the data. For the get.activities() function, I adapted code from here.

library(httr)
library(rjson)
library(OpenStreetMap)
library(leaflet)
library(scales)
library(dplyr)

token ”

get.coord.df.from.stream
data.frame(lat = sapply(stream.obj[[1]]$data, USE.NAMES = F, FUN = function (x) x[[1]]),
lon = sapply(stream.obj[[1]]$data, USE.NAMES = F, FUN = function (x) x[[2]]))
}

get.stream.from.activity
stream
path = paste0(“api/v3/activities/”, act.id, “/streams/latlng”),
query = list(access_token = token))
content(stream)
}

get.activities
activities
query = list(access_token = token, per_page = 200))
activities
activities
activities
x[sapply(x, is.null)]
unlist(x)
})
data.frame(do.call(“rbind”, activities))
}

get.multiple.streams
res.list
for (act.id.i in 1:length(act.ids)) {
if (act.id.i %% 5 == 0) cat(“Actitivy no.”, act.id.i, “of”, length(act.ids), “n”)
stream
coord.df
res.list[[length(res.list) + 1]]
coords = coord.df)
}
res.list
}

We have all the functions we need to get and parse the APIs output available now. Let’s apply them. The logic is: First, we get all activities. This dataframe has a column called ‘id’ which we can use to get all the raw data for all activities (called ‘streams’ in the Strava API). The function get.coord.df.from.stream() creates a dataframe with lat/lon coordinates for one stream.

activities
stream.list

We might want to get the boundaries of the cumulated set of all streams. We can use these boundaries as a bounding box for plotting the data. This means that all activities are going to be in the plotted map section.

all.lats
x$coords$lat
}))
all.lons
x$coords$lon
}))
lats.range
lons.range

Alternatively, you can set your own bounding box. These are the boundaries for Stuttgart, Germany. One suggestion: to find your own boundaries, you can plot your first map and use the locator() function in RStudio, this is a very convenient way of getting coordinates by clicking.
lons.range
lats.range

We start by plotting the tracks in red against a black background. You can play around with the alpha value and the lwd parameter to change the appearance. By plotting several tracks over each other, thicker lines represent routes I took more often.

# Setting up the plot
par(bg = “black”)
plot(x = lons.range, y = lats.range, type = “n”, bty = “n”, xlab = “”, ylab = “”, xaxt = “n”, yaxt = “n”)

# Plotting tracks one by one
for (el in stream.list) {
lines(el$coords$lon, el$coords$lat,
col = alpha(“darkred”, .4), lwd = 2)
}

A black-and-red plot of my runs through Stuttgart.
Now, this already looks quite nice and we see some kind of network through the city. But the city itself is missing. We need to get some map below the tracks. I am using the OpenStreetMap package for this. I already used it in an earlier post. Note that getting the map tiles from the servers might take a long time and might fail in some cases. Loading time (and the resolution of the final map) will depend heavily on the ‘zoom’ parameter.
map
c(min(lats.range), max(lons.range)), type = “maptoolkit-topo”, zoom = 14)
transmap
plot(transmap, raster = T)

for (el in stream.list) {
lines(el$coords$lon, el$coords$lat,
col = alpha(“darkred”, .5), lwd = 3)
}
My runs through Stuttgart on an OpenStreetMap, click to enlarge

I also like a simple satellite map. I am using the ‘bing’ map type for this. There is a high-def version available here.
map
c(min(lats.range), max(lons.range)), type = “bing”, zoom = 15)
transmap
plot(transmap, raster = T)

for (el in stream.list) {
lines(el$coords$lon, el$coords$lat,
col = alpha(“yellow”, 1/3), lwd = 3)
}
My runs through Stuttgart on a Bing satellite map, click to enlarge or click here for an HD version

Now, for the final step. These static maps already look quite nice and with sufficient resolution (as in the HD case with the satellite map), we can also zoom the map without loosing too much quality. But a more dynamical map would also be nice. Let’s use the wonderful leaflet package for this. I already done this in another post with only a single track wrapped in a Shiny app. I am using some pipe notation from the dplyr package to adapt the map and get the tracks onto it.
map %
addProviderTiles(“CartoDB.Positron”,
options = providerTileOptions(noWrap = T)) %>%
fitBounds(lng1 = min(lons.range), lat1 = max(lats.range), lng2

for (el in stream.list) {
map
color = “red”, opacity = 1/3, weight = 2)
}
With the function saveWidget(), we can save the resulting html map. You can move the map to Basel or Zürich to find some more tracks I ran there.

With the leaflet functions, we could even associate each track with a little mouseover text (like total distance or the date). I did not include this here because quite a few tracks have been plotted over each other and mouseover texts might just confuse us here.

Have fun running and plotting.

To leave a comment for the author, please follow the link and comment on their blog: Rcrastinate.

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more…

Source:: R News

Leave a Reply

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

Time limit is exhausted. Please reload CAPTCHA.