Rayshading the Big Sur Marathon


May 24, 2020

Rendered image of Big Sur, California developed with rayshader package

Visualizing marathon races

With marathons and other large gatherings canceled or delayed by the COVID-19 pandemic, I’ve been feeling extra nostalgic for past running experiences in some of my favorite places. At the top of the list is the Big Sur Marathon that runs along California State Route 1 from Big Sur Station to Carmel. The course starts near the southern edge of the Redwood forest, rises to the top of Hurricane Point overlooking the Pacific, crosses iconic Bixby Creek Bridge, and passes over numerous hills and turns before reaching the finish. Big Sur is a challenging spring marathon, and while I finished in just over 4 hours in 2019, it was definitely the most difficult race I’ve run. Having a live piano performance at Bixby helped though.

Photo of pianist at Bixby Creek Bridge during the Big Sur Marathon

Like other runners, I tracked my progress with GPS, using the RunKeeper app on my phone. This project will use the GPS location data from RunKeeper and visualize the course using the rayshader R package, which can provide a 3-dimensional view of a landscape or other surface. Tyler Morgan-Wall recently posted an excellent guide for combining elevation data with satellite imagery to create 3D renderings of landscapes. Examples from Sebastian Engel-Wolf, Simon Coulombe, and Francois Keck of using animation with rayshader also provided plenty of guidance and inspiration.

Data sources and prep

This project will need the GPS data from RunKeeper, satellite imagery data, and elevation data. Morgan-Wall’s post includes the step-by-step process for downloading the SRTM elevation data and imagery data from USGS. I won’t repeat those steps here, but I’ve pre-downloaded the data for the Big Sur region of California.

Let’s get to it and load the R packages.


First, let’s load the elevation data. I downloaded data for two bordering areas, so I’ll merge them together.

elevation1 <- raster::raster(file.path(path_to_data, "N36W122.hgt"))
elevation2 <- raster::raster(file.path(path_to_data, "N36W123.hgt"))

bigsur_elevation <- raster::merge(elevation1,elevation2)

Next, let’s load the satellite data, which comes in three files that are separate layers for the image. After loading the layers, we’ll combine them and apply a color adjustment. It will need more refinement, but the Monterey peninsula is visible.

bigsur_r <- raster::raster(file.path(path_to_data, "LC08_L1TP_044035_20191026_20191030_01_T1_B4.TIF"))
bigsur_g <- raster::raster(file.path(path_to_data, "LC08_L1TP_044035_20191026_20191030_01_T1_B3.TIF"))
bigsur_b <- raster::raster(file.path(path_to_data, "LC08_L1TP_044035_20191026_20191030_01_T1_B2.TIF"))

bigsur_rbg_corrected <- sqrt(raster::stack(bigsur_r, bigsur_g, bigsur_b))

Finally, let’s load the GPS data from RunKeeper, which comes as a GPX file. With a quick view using mapView, we can see the tracked points from the GPS file are in the right locations.

bigsur_gpx <-
    file.path(path_to_data, "RK_gpx_2019-04-28_0643.gpx"),
    layer = "track_points",
    quiet = TRUE
  ) %>%
  dplyr::select(track_seg_point_id, ele, time, geometry)