On bike around chimney

Nothing special, just a few words from last cycling. And a way to show the track and couple of photos on the map.
R
rStrava
leaflet
maps
Author

Grzegorz Sapijaszko

Published

February 7, 2022

When the winds blows, it’s time for bike.

Strava and R

Cycling is my way of daily commuting. It’s also a way to go here and there, usually with panniers and hammock in them. I though it would be nice to illustrate the post with track on map. Started to looking around what’s available to process GPX tracks in R and found a few examples; there were attempts using XML package (Lizarazo 2018; Wolfer 2014), there are some promising functions: readGPX() in plotKLM or read_GPX() in tmaptools package.

I’m using Strava for long time as odometer and track logger. How I was surprised when found rStrava package. (Villarroel et al. 2021). It allows you to get the Strava activities, run couple of analysis, and together with Google API key — create a heat maps. But let’s see what’s inside (and how to use it). To get the access to Strava you have to create Strava API key - the whole procedure is well described in readme

After creating API key and configuring the token, we can access the data:

stoken <- httr::config(token = readRDS('.httr-oauth')[[1]])

Let’s grab basic information about myself :)

myinfo <- get_athlete(stoken, id = '16504523')
myinfo$username
[1] "gsapijaszko"

Most functions of rStrava package depends on Google Maps API, however I would like to show a bit different approach. As we can get the activities and process it independently, let’s get some data:

my_acts <- get_activity_list(stoken, 
             after = as.Date("2021-08-07"), 
             before = as.Date("2021-08-15"))

The output of get_activity_list() returns a list of activities. We can convert it to more easy to work with format like data.table with compile_activities() function. The resulting data frame has 54 variables, just showing few of them:

my_data <- compile_activities(my_acts)
my_data[1:2, c("id", "distance", "average_speed", "max_speed", "total_photo_count")]
          id distance average_speed max_speed total_photo_count
1 5792096677  66.4123       15.3900     37.80                 3
2 5786872981  38.4768       11.0592     31.32                 0

The activity geometry received from Strava is encoded by Google polyline algorithm. To decode it we will use googlePolylines package (Cooley 2020a). To apply calculation to all polylines in data frame we will build a small function. It takes polyline as argument, decode it, converts to linestring (using sf_linestring() from sfheaders package (Cooley 2020b)) and assigning proper CRS by st_crs() from sf package:

decPolilineAsLinestring <- function (polyline) {
  b <- as.data.frame(googlePolylines::decode(polyline))
  c <- sfheaders::sf_linestring(b, x="lon", y="lat")
  sf::st_crs(c) <- 4326
  return (c$geometry)
}

Let’s apply the function to our data:

my_data <- my_data |>
  dplyr::rowwise() |>
  dplyr::mutate(geometry = decPolilineAsLinestring(map.summary_polyline))

And finally, we can plot it using leaflet package:

library(leaflet)
leaflet(my_data$geometry) |>
  addTiles() |>
  addPolylines()

Easy, isn’t it?

But what about photos? Why not to add them to the map? We could reuse the photos which are uploaded to Strava (do you remember total_photo_count variable from my_data table?), however for some reasons Strava not always keeps the exif coordinates within the photo. Therefore photos will be stored locally, and we will use exiftoolr package (O’Brien 2021) to access their coordinates.

library(exiftoolr)
photos <- exif_read(path = "photos", recursive = TRUE)
Using ExifTool version 12.49

And extend our map:

map <- leaflet(my_data$geometry)
map <- addTiles(map)
map <-   addPolylines(map, color = "#0000FF")
for (i in 1:nrow(photos)) {
  map <- addMarkers(map, 
                    lng = photos[i,"GPSLongitude"], 
                    lat = photos[i,"GPSLatitude"], 
                    popup = paste0("<a href = photos/", photos[i, "FileName"], 
                            " target = \"_blank\">", "<img src = photos/", 
                            photos[i, "FileName"], " width = \"300\">", "</a>"))
}
map

References

Cooley, David. 2020a. googlePolylines: Encoding Coordinates into Google Polylines. https://CRAN.R-project.org/package=googlePolylines.
———. 2020b. Sfheaders: Converts Between r Objects and Simple Feature Objects. https://dcooley.github.io/sfheaders/.
Lizarazo, Ivan. 2018. RPubs - Reading and Plotting GPX with R.” October 31, 2018. https://rpubs.com/ials2un/gpx1.
O’Brien, Joshua. 2021. Exiftoolr: ExifTool Functionality from r. https://github.com/JoshOBrien/exiftoolr#readme.
Villarroel, Pedro, Daniel Padfield, Lorenzo Gaborini, and Niklas von Maltzahn. 2021. rStrava: Access the Strava API.
Wolfer, Sascha. 2014. “Stay on Track: Plotting GPS Tracks with R | R-bloggers.” September 18, 2014. https://www.r-bloggers.com/2014/09/stay-on-track-plotting-gps-tracks-with-r/.