Where do you run to? Map your Strava activities on static and Leaflet maps.
This article is originally published at https://rcrastinate.blogspot.com/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 <- "<access token goes here>"
get.coord.df.from.stream <- function (stream.obj) {
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 <- function (act.id, token) {
stream <- GET("https://www.strava.com/",
path = paste0("api/v3/activities/", act.id, "/streams/latlng"),
query = list(access_token = token))
content(stream)
}
get.activities <- function (token) {
activities <- GET("https://www.strava.com/", path = "api/v3/activities",
query = list(access_token = token, per_page = 200))
activities <- content(activities, "text")
activities <- fromJSON(activities)
activities <- lapply(activities, function(x) {
x[sapply(x, is.null)] <- NA
unlist(x)
})
data.frame(do.call("rbind", activities))
}
get.multiple.streams <- function (act.ids, token) {
res.list <- 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 <- get.stream.from.activity(act.ids[act.id.i], token)
coord.df <- get.coord.df.from.stream(stream)
res.list[[length(res.list) + 1]] <- list(act.id = act.ids[act.id.i],
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 <- get.activities(token)
stream.list <- get.multiple.streams(activities$id, token)
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 <- unlist(sapply(stream.list, USE.NAMES = F, FUN = function (x) {
x$coords$lat
}))
all.lons <- unlist(sapply(stream.list, USE.NAMES = F, FUN = function (x) {
x$coords$lon
}))
lats.range <- range(all.lats)
lons.range <- range(all.lons)
# 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)
}
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.
Thanks for visiting r-craft.org
This article is originally published at https://rcrastinate.blogspot.com/
Please visit source website for post related comments.