Creating a Heatmap in R with Google Polylines

The rStrava package allows easy access to your Strava data using the Strava API. Combined with leaflet I can easily create a heatmap of all of my Strava activities. One interesting challenge does arise when doing this. Strava stores the coordinates of the activity as a Google Polyline, so I will use the googleway package to decode the data in to latitude and longitude coordinates.

First, I load in all the necessary packages.

# Code written by Daniel Cullen
# Using code from the following sources:
# https://padpadpadpad.github.io/post/animate-your-strava-activities-using-rstrava-and-gganimate/ 
# https://medium.com/@annthurium/getting-started-with-the-strava-api-a-tutorial-f3909496cd2d 
# https://www.r-bloggers.com/where-do-you-run-to-map-your-strava-activities-on-static-and-leaflet-maps/ 
# http://www.databrew.cc/posts/strava.html 
# https://github.com/fawda123/rStrava

library(rStrava)     # devtools::install_github('fawda123/rStrava') 
library(dplyr)
library(leaflet)
library(htmlwidgets)
library(googleway)   # to convert google polyline format to lat and long

Then I will enter my personal Strava API information which can be found here after you apply for API access.

## Necessary info from Strava api https://www.strava.com/settings/api 
app_name       <- '<>' # chosen by user
app_client_id  <- '<>' # an integer, assigned by Strava
app_secret     <- '<>' # an alphanumeric secret, assigned by Strava

stoken <- httr::config(token = strava_oauth(app_name, app_client_id, app_secret, app_scope="activity:read_all"))

Next, I load in my activity list and compile my activities into a data frame using the rStrava library.

## Load my activities and compile activities with rStrava library
my_data  <- get_activity_list(stoken)
act_data <- compile_activities(my_data)

The activity data contains 56 variables. We only need to keep two, the map data called 'map.summary_polyline' and the 'upload_id'.

## Keep only activity id and map line
keeps   <- c('map.summary_polyline', 'upload_id')
my_acts <- dplyr::select(act_data, match(keeps, names(act_data)))

Now that the data is prepared I want to create a blank map of the area I want to include in my map. I create longitude and latitude bounds to create the map area I want. This will take some trial and error to get the exact bounds that you want.

## Create blank map bounded by given lon and lat
lons.range <- c(-119.9, -119.6)
lats.range <- c(34.3, 34.58)
## tile options CartoDB.Positron , CartoDB.DarkMatter , Stamen.Toner  
map <- leaflet(options = leafletOptions(zoomControl = FALSE)) %>%
  addProviderTiles('CartoDB.Positron',
                   options = providerTileOptions(noWrap = T, minZoom=12, maxZoom=12)) %>%
  fitBounds(lng1 = min(lons.range), lat1 = max(lats.range), lng2 <- max(lons.range), lat2 = min(lats.range))
map   #save using mapshot(map, file="sbmap.png")

Next I loop through all my activities and convert them from Google Polylines to a dataframe of longitudes and latitudes each loop through will add one activity to the map.

loop <- unique(my_acts$upload_id)
for (i in loop) {
  activity <- filter(my_acts, upload_id == i)
  coords   <- googleway::decode_pl(activity$map.summary_polyline)
  map      <- addPolylines(map, lng = coords$lon, lat = coords$lat,
                      color = 'blue', opacity = 1/4, weight = 2)
}

map

Finally, to export the final interactive map as seen at the top of the page I use the saveWidget command from the htmlwidgets package.

saveWidget(map, file="runningmap.html")