An easy tutorial to make impressive topological maps with R & Rayshader.
By: Nathanael Sheehan
Tags: GIS, Maps, Cartography, R, Rayshader

Within the R community, there are some pretty clever people making pretty clever packages. By far one of the most impressive packages is the ‘Rayshader’ package developed by Tyler Morgan-Wall.

This package enables users to create 2d and 3d visualisations using elevation data. Through a clever combination of “ raytracing, spherical texture mapping, overlays, and ambient occlusion” users can produce stunning topological maps and animations. For those familiar with R, users are now able to convert ggplot2 plots into 3d visualisations … but I’ll get into that in another tutorial.

Now, while Rayshader is super impressive, it is also pretty daunting in its application. So here is a simple tutorial for you to impress your cartography friends with.

Firstly you need to install and load the following packages



install.packages("rayshader", repos = "

require(rayshader)

}

if (!require(geoviz)) {

install.packages("geoviz", repos = "

require(geoviz)

}

if (!require(tibble)) {

install.packages("tibble", repos = "

require(tibble)

}

if (!require(dplyr)) {

install.packages("dplyr", repos = "

require(dplyr)

}

if (!require(ggplot2)) {

install.packages("ggplot2", repos = "

require(ggplot2)

}

if (!require(raster)) {

install.packages("raster", repos = "

require(raster)

}

if (!require(Magick)) {

install.packages("Magick", repos = "

require(Magick)

} if (!require(rayshader)) {install.packages("rayshader", repos = " http://cran.us.r-project.org ")require(rayshader)if (!require(geoviz)) {install.packages("geoviz", repos = " http://cran.us.r-project.org ")require(geoviz)if (!require(tibble)) {install.packages("tibble", repos = " http://cran.us.r-project.org ")require(tibble)if (!require(dplyr)) {install.packages("dplyr", repos = " http://cran.us.r-project.org ")require(dplyr)if (!require(ggplot2)) {install.packages("ggplot2", repos = " http://cran.us.r-project.org ")require(ggplot2)if (!require(raster)) {install.packages("raster", repos = " http://cran.us.r-project.org ")require(raster)if (!require(Magick)) {install.packages("Magick", repos = " http://cran.us.r-project.org ")require(Magick)

Next, you need to set some parameters for the area you would like to visualise. In this example, I use coordinates for Cornwall (mostly because I miss my family farm down there).

# Set lat long for area you would like to rayshade - this example looks at Plymouth to Cornwall lat = 50.3755

lon = -4.1427 # Set the km2 radius of the area

square_km = 80 # Max tiles request fron 'mapzen' and 'stamen' - Increasing max_tiles results in a high res image (but will take more time) max_tiles = 40

Following this, you will need to fetch the elevation data based on your pre-determined parameters. (This may take a few moments depending on the size of your area)

dem <- mapzen_dem(lat, lon, square_km, max_tiles = max_tiles)

You have two options as a map overlay. In this example, I use a Stamen terrain overlay, as I wanna show off the natural vegetation of Cornwall’s landscape. If the terrain overlay is not for you, check out the other range of stamen overlays here -> http://maps.stamen.com/#toner/12/37.7706/-122.3782).

Alternatively, you can use a Mapbox overlay, however, in doing so you will also need to remember to add your API key into this request.

# Get a stamen overlay

overlay_image <-

slippy_overlay(dem,

image_source = "stamen",

image_type = "terrain",

png_opacity = 0.3,

max_tiles = max_tiles)

Once this is completed we want to render the ray shader scene into a base R matrix.

# Render the 'rayshader' scene

heightmap = matrix(

raster::extract(dem, raster::extent(dem), method = 'bilinear'),

nrow = ncol(dem),

ncol = nrow(dem)

)

Next, you need to add some shading. Typically I go for a sun angle of 270, yet depending on the map I am creating I will vary the texture I use. Rayshader comes with seven built-in styles “imhof1”, “imhof2”, “imhof3”, imhof4“,”desert“,”bw“ and ”unicorn”.

sunshade <- heightmap %>%

sphere_shade(sunangle = 270, texture = "imhof1") %>%

add_overlay(overlay_image)

Now you’re ready to plot. We add the sunshade and heightmap into the plot3d function along with a few other variables. It is important to note, if your visualisation is of a landlocked area there is no need add any of the water variables to the function. However, if you love the sea as much as I do, Rayshader has some pretty awesome features to display water.

rayshader::plot_3d(

sunshade,

heightmap,

zscale = raster_zscale(dem) / 3,

solid = TRUE,

shadow = FALSE,

soliddepth = -raster_zscale(dem),

water=TRUE,

waterdepth = 0.5,

wateralpha = 0.5,

watercolor = "lightblue",

waterlinecolor = "white",

waterlinealpha = 0.5

)

As my wise GIS teacher always taught me, if you’re publishing these maps in a journal or report its useful to include a scale bar and north arrow. Rayshader can do this in two simple lines of code 💥

#add scale bar to plot

render_scalebar(limits=c(0, 5, 10),label_unit = "km",position = "W", y=50,scale_length = c(0.33,1))

(position = "E") #add north arrow to plot render_compass (position = "E")

Finally, you can render the plot in high quality and export to png

render_highquality(samples=200, scale_text_size = 24,clear=TRUE) rayshader::render_depth(

filename = "cornwall.png")

And there you have it. A simple way to make super impressive topological maps with Rayshader. A massive shout out to the mind-boggling work of Tyler, and if you wish to learn more yourself checkout the Rayshaer documentation https://www.rayshader.com/index.html