Welcome to part 2 of how geoviz can help you to quickly draw 3D scenes using the rayshader package. If you haven’t read part 1 yet, you might want to start here.
Still reading? Then let’s put some GPS data in a rayshader scene!
You can use any GPS data that you want, but depending on what you’re loading, it will probably need a bit of processing. You need to get to three vectors; lat and long in EPSG:4326 projection (Google Maps coordinates) and also altitude if your track is a flight.
Glider pilots commonly use the IGC track format and geoviz has a helper function to process these, so let’s download one for this example. This first snippet will grab a paraglider flight in the mountains of Scotland.
library(rayshader)
library(geoviz)
igc_file <- download.file("http://www.xcleague.com/xc/download.php?pilot=1&output=IGC&year=2018&xcFlightId=20182909", "example.igc")
tracklog <- read_igc("example.igc")
If your GPS trace is in .gpx format, then the plotKML package has a handy function readGPX(), which can help. If your trace is in any other format then you're on your own, but GPS Visualiser might help initially, to manually turn it into something that you can work with in R.
When we used geoviz in part 1, we gave it a single lat-long coordinate as the centre for our scene. Pass the lat and long vectors from a GPS trace instead and geoviz will helpfully make a rectangular area to contain them.
#Get elevation data from Mapzen
#Increase this to ~60 for a higher resolution (but slower) image
max_tiles <- 10
dem <- mapzen_dem(tracklog$lat, tracklog$long, max_tiles = max_tiles)
Let's make a Stamen overlay to save messing about with Mapbox API keys. Part 1 gave you the tools to do satellite overlays instead if you want to.
#create an overlay image
overlay_image <-
slippy_overlay(
dem,
image_source = "stamen",
image_type = "watercolor",
png_opacity = 0.3,
max_tiles = max_tiles
)
#Turn mountainous parts of the overlay transparent (optional but pretty)
overlay_image <-
elevation_transparency(overlay_image,
dem,
pct_alt_high = 0.5,
alpha_max = 0.9)
Render the scene...
#Draw the rayshader scene
elmat = matrix(
raster::extract(dem, raster::extent(dem), method = 'bilinear'),
nrow = ncol(dem),
ncol = nrow(dem)
)
sunangle <- 270
scene <- elmat %>%
sphere_shade(sunangle = sunangle, texture = "desert") %>%
add_overlay(overlay_image) #%>%
#Uncomment these lines and the %>% above for better shadows but a much slower render
# add_shadow(
# ray_shade(
# elmat,
# anglebreaks = seq(30, 60),
# sunangle = sunangle,
# multicore = TRUE,
# lambert = FALSE,
# remove_edges = FALSE
# )
# ) %>%
# add_shadow(ambient_shade(elmat, multicore = TRUE, remove_edges = FALSE))
rayshader::plot_3d(
scene,
elmat,
zscale = raster_zscale(dem),
solid = FALSE,
shadow = TRUE,
shadowdepth = -150
)
And now add your GPS trace
add_gps_to_rayshader(
dem,
tracklog$lat,
tracklog$long,
tracklog$altitude,
line_width = 1,
lightsaber = TRUE,
zscale = raster_zscale(dem),
ground_shadow = TRUE,
colour = "red"
)

All done! Scotland's looking a bit desert-like but that's the Stamen overlay and rayshader "desert" theme working together. Play with the options and you can generate all sorts of different results. You can use the same technique to plot any route or track that you like, just massage your data into a set of lat-long vectors and you're there.
If your GPS trace is ground based, you'll probably want to use clamp_to_ground = TRUE and the raise_agl option of add_gps_to_rayshader() to offset its altitude a little so that it doesn't clash with spikes in the terrain. If your track points don't have altitude, just give add_gps_to_rayshader() any arbitrary number for altitude and then set clamp_to_ground = TRUE.