Getting Over It

Visualising my Transalp bike ride

Julian During
2026-01-15

In summer 2020, I crossed the Alps with my road bike. I’ve recorded the whole ride and as a nice memory, I would like to visualise this ride.

Data

I collected the data using my GPS head unit and uploaded it to Strava. In this analysis it is available in its entirety as two .rds files.

To load, wrangle and visualise the data, the following libraries are used:

Define where exactly the data is located.

act_file <- "data/act.rds"
meas_file <- "data/meas.rds"

Read activity data from rds file.

act <- function(act_file) {
  read_rds(act_file) |>
    mutate(
      start_name = case_when(
        id == "3650448726" ~ "Albstadt",
        id == "3654245140" ~ "Winterthur",
        id == "3659045033" ~ "Fluelen",
        id == "3664650034" ~ "Andermatt",
        id == "3669729902" ~ "Andermatt",
        TRUE ~ NA_character_),
      end_name = case_when(
        id == "3650448726" ~ "Winterthur",
        id == "3654245140" ~ "Fluelen",
        id == "3659045033" ~ "Andermatt",
        id == "3664650034" ~ "Andermatt",
        id == "3669729902" ~ "Lugano",
        TRUE ~ NA_character_),
      poi_name = if_else(end_name == "Lugano", "Lugano", start_name),
      name = str_glue("{start_name} - {end_name}"),
      lat_poi = if_else(
        poi_name == "Lugano",
        map_dbl(end_latlng, 1),
        map_dbl(start_latlng, 1)),
      lng_poi = if_else(
        poi_name == "Lugano",
        map_dbl(end_latlng, 2),
        map_dbl(start_latlng, 2))) |>
    select(-distance)
}
df_act <- act(act_file)

Read measurement data. This data represents each point in time during my trip across the Alps. This includes among other things:

meas <- function(meas_file) {
  df_meas_raw <- read_rds(meas_file)

  df_distance_max <- df_meas_raw |>
    group_by(id) |>
    summarise(distance_max = max(distance)) |>
    mutate(
      distance_max = lag(distance_max, default = 0),
      distance_max = cumsum(distance_max))

  df_meas_raw |>
    left_join(df_distance_max, by = join_by(id)) |>
    group_by(id) |>
    mutate(distance_cum = distance + distance_max) |>
    ungroup()
}
df_meas <- meas(meas_file)

Turn measurements into an sf (Pebesma and Bivand 2023) object:

act_meas_points <- function(df_meas, df_act) {
  df_meas |>
    st_as_sf(coords = c("lng", "lat"), crs = 4326) |>
    left_join(df_act, by = join_by(id))
}
sf_act_meas_points <- act_meas_points(df_meas, df_act)

The sf object represents the ride as point data. We combine it into one LINESTRING:

act_meas_lines <- function(sf_act_meas_points) {
  sf_act_meas_points |>
    group_by(id, name, lng_poi, lat_poi, poi_name) |>
    summarise(
      mean_speed = mean(velocity_smooth),
      mean_altitude = mean(altitude),
      mean_distance = mean(distance_cum),
      do_union = FALSE, .groups = "drop") |>
    st_cast("LINESTRING")
}
sf_act_meas_lines <- act_meas_lines(sf_act_meas_points)

With the help of the ‘elevatr’ package (Hollister et al. 2025), get the elevation data of the trip. Turn it into an ‘raster’ (Hijmans 2025) object for later plotting.

alpen_raster <- function(sf_lines) {
  get_elev_raster(sf_lines, z = 6, clip = "bbox", expand = 0.1) |>
    rast()
}
raster_alpen <- alpen_raster(sf_act_meas_lines)

Visualisation

Start with creating a map of the ride.

vis_ride <- function(sf_act_meas_lines, raster_alpen) {
  ggplot(sf_act_meas_lines) +
    geom_spatraster_contour(data = raster_alpen, binwidth = 200) +
    geom_sf(aes(color = id)) +
    geom_label_repel(
      aes(x = lng_poi, y = lat_poi, label = poi_name),
      size = 2.5, fill = alpha(c("white"), 0.5)
    ) +
    labs(
      x = "Longitude", y = "Latitude"
    ) +
    theme(
      legend.position = "none", panel.grid.major = element_blank(),
      axis.text = element_blank(), axis.title = element_blank()
    )
}

If we were to create a plot with this function, the result would look like this:

The main challenge of the ride was the enormous elevation that was covered. Display this in an altitude plot:

vis_altitude <- function(sf_act_meas_points, sf_act_meas_lines) {
  sf_act_meas_points |>
    filter(moving) |>
    ggplot(aes(x = distance_cum, y = altitude, color = id, group = id, fill = id)) +
    geom_area(alpha = 0.6) +
    geom_line() +
    geom_label(
      data = sf_act_meas_lines,
      mapping = aes(x = mean_distance, y = mean_altitude, label = name),
      fill = alpha(c("white"), 0.5)
    ) +
    labs(x = "Distance [km]", y = "Height [m]") +
    theme(legend.position = "none") +
    expand_limits(y = 0) +
    scale_x_continuous(
      labels = label_number(scale = 0.001),
      breaks = breaks_width(50000)
    ) +
    scale_y_continuous(position = "right")
}

The plot would look like this:

Combine the two plots into one with the help of the patchwork (Pedersen 2025) package:

vis_transalp <- function(sf_act_meas_points, sf_act_meas_lines, raster_alpen) {
  gg_final <- vis_ride(sf_act_meas_lines, raster_alpen) +
    vis_altitude(sf_act_meas_points, sf_act_meas_lines) +
    plot_layout(widths = c(1, 3)) +
    plot_annotation(
      title = "Transalp 2020",
      subtitle = "Albstadt - Lugano"
    ) &
    theme(
      text = element_text(family = "Fira Code", size = 12)
    )

  ggsave(
    "transalp.png", gg_final,
    width = 40, height = 25, units = "cm", scale = 0.8
  )
}
gg_transalp <- vis_transalp(sf_act_meas_points, sf_act_meas_lines, 
     raster_alpen)

What a ride it has been! I’m always thinking back to this experience. It’s time to recreate such a ride in the near future.

Acknowledgments

This work was completed using R v. 4.4.1 (R Core Team 2024) and the following R packages: distill v. 1.6 (Dervieux et al. 2023), elevatr v. 0.99.1 (Hollister et al. 2025), fs v. 1.6.6 (Hester, Wickham, and Csárdi 2025), geotargets v. 0.3.1 (Tierney, Scott, and Brown 2024), ggrepel v. 0.9.6 (Slowikowski 2024), ggtext v. 0.1.2 (Wilke and Wiernik 2022), knitr v. 1.51 (Xie 2014, 2015, 2025), magick v. 2.9.0 (Ooms 2025), patchwork v. 1.3.2 (Pedersen 2025), pins v. 1.4.1 (Silge, Wickham, and Luraschi 2025), raster v. 3.6.32 (Hijmans 2025), rmarkdown v. 2.30 (Xie, Allaire, and Grolemund 2018; Xie, Dervieux, and Riederer 2020; Allaire et al. 2025), scales v. 1.4.0 (Wickham, Pedersen, and Seidel 2025), sf v. 1.0.23 (Pebesma 2018; Pebesma and Bivand 2023), shiny v. 1.12.1 (Chang et al. 2025), tarchetypes v. 0.13.2 (Landau 2021a), targets v. 1.11.4 (Landau 2021b), terra v. 1.8.93 (Hijmans 2026), tidyterra v. 0.7.2 (Hernangómez 2023), tidyverse v. 2.0.0 (Wickham et al. 2019).

Allaire, JJ, Yihui Xie, Christophe Dervieux, Jonathan McPherson, Javier Luraschi, Kevin Ushey, Aron Atkins, et al. 2025. rmarkdown: Dynamic Documents for r. https://github.com/rstudio/rmarkdown.
Chang, Winston, Joe Cheng, JJ Allaire, Carson Sievert, Barret Schloerke, Garrick Aden-Buie, Yihui Xie, et al. 2025. shiny: Web Application Framework for r. https://CRAN.R-project.org/package=shiny.
Dervieux, Christophe, JJ Allaire, Rich Iannone, Alison Presmanes Hill, and Yihui Xie. 2023. distill: R Markdown Format for Scientific and Technical Writing. https://CRAN.R-project.org/package=distill.
Hernangómez, Diego. 2023. “Using the tidyverse with terra Objects: The tidyterra Package.” Journal of Open Source Software 8 (91): 5751. https://doi.org/10.21105/joss.05751.
Hester, Jim, Hadley Wickham, and Gábor Csárdi. 2025. fs: Cross-Platform File System Operations Based on libuv. https://CRAN.R-project.org/package=fs.
Hijmans, Robert J. 2025. raster: Geographic Data Analysis and Modeling. https://CRAN.R-project.org/package=raster.
———. 2026. terra: Spatial Data Analysis. https://CRAN.R-project.org/package=terra.
Hollister, Jeffrey, Tarak Shah, Jakub Nowosad, Alec L. Robitaille, Marcus W. Beck, and Mike Johnson. 2025. elevatr: Access Elevation Data from Various APIs. https://doi.org/10.5281/zenodo.8335450.
Landau, William Michael. 2021a. tarchetypes: Archetypes for Targets.
———. 2021b. “The Targets r Package: A Dynamic Make-Like Function-Oriented Pipeline Toolkit for Reproducibility and High-Performance Computing.” Journal of Open Source Software 6 (57): 2959. https://doi.org/10.21105/joss.02959.
Ooms, Jeroen. 2025. magick: Advanced Graphics and Image-Processing in r. https://CRAN.R-project.org/package=magick.
Pebesma, Edzer. 2018. Simple Features for R: Standardized Support for Spatial Vector Data.” The R Journal 10 (1): 439–46. https://doi.org/10.32614/RJ-2018-009.
Pebesma, Edzer, and Roger Bivand. 2023. Spatial Data Science: With applications in R. Chapman and Hall/CRC. https://doi.org/10.1201/9780429459016.
Pedersen, Thomas Lin. 2025. patchwork: The Composer of Plots. https://CRAN.R-project.org/package=patchwork.
R Core Team. 2024. R: A Language and Environment for Statistical Computing. Vienna, Austria: R Foundation for Statistical Computing. https://www.R-project.org/.
Silge, Julia, Hadley Wickham, and Javier Luraschi. 2025. pins: Pin, Discover, and Share Resources. https://CRAN.R-project.org/package=pins.
Slowikowski, Kamil. 2024. ggrepel: Automatically Position Non-Overlapping Text Labels with ggplot2. https://CRAN.R-project.org/package=ggrepel.
Tierney, Nicholas, Eric Scott, and Andrew Brown. 2024. geotargets: Targets Extensions for Geospatial Formats.” https://docs.ropensci.org/geotargets/.
Wickham, Hadley, Mara Averick, Jennifer Bryan, Winston Chang, Lucy D’Agostino McGowan, Romain François, Garrett Grolemund, et al. 2019. “Welcome to the tidyverse.” Journal of Open Source Software 4 (43): 1686. https://doi.org/10.21105/joss.01686.
Wickham, Hadley, Thomas Lin Pedersen, and Dana Seidel. 2025. scales: Scale Functions for Visualization. https://CRAN.R-project.org/package=scales.
Wilke, Claus O., and Brenton M. Wiernik. 2022. ggtext: Improved Text Rendering Support for ggplot2. https://CRAN.R-project.org/package=ggtext.
Xie, Yihui. 2014. knitr: A Comprehensive Tool for Reproducible Research in R.” In Implementing Reproducible Computational Research, edited by Victoria Stodden, Friedrich Leisch, and Roger D. Peng. Chapman; Hall/CRC.
———. 2015. Dynamic Documents with R and Knitr. 2nd ed. Boca Raton, Florida: Chapman; Hall/CRC. https://yihui.org/knitr/.
———. 2025. knitr: A General-Purpose Package for Dynamic Report Generation in R. https://yihui.org/knitr/.
Xie, Yihui, J. J. Allaire, and Garrett Grolemund. 2018. R Markdown: The Definitive Guide. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/rmarkdown.
Xie, Yihui, Christophe Dervieux, and Emily Riederer. 2020. R Markdown Cookbook. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/rmarkdown-cookbook.

References

Corrections

If you see mistakes or want to suggest changes, please create an issue on the source repository.

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY 4.0. Source code is available at https://codeberg.org/duju211/transalp_codeberg, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".

Citation

For attribution, please cite this work as

During (2026, Jan. 15). Datannery: Getting Over It. Retrieved from https://www.datannery.com/posts/getting-over-it/

BibTeX citation

@misc{during2026getting,
  author = {During, Julian},
  title = {Datannery: Getting Over It},
  url = {https://www.datannery.com/posts/getting-over-it/},
  year = {2026}
}