Introducing 3D ggplots with Rayshader (R)
Rayshader is a powerful package in R supporting 2D and 3D data visualisation. What amazes me is its details of displaying the 3D plots and it provides lots of customisation. Most importantly, it allows you to directly transform the ggplot2 objects into 3D plot.

In this demonstration, the Hong Kong population and housing price data are used. Pre-processed data and the R scripts are uploaded to my Github page. Feel free to download and play around.

Package
rayshader
Functionality
rayshader
is an open-source package for producing 2D and 3D data visualizations in R. rayshader
uses elevation data in a base R matrix and a combination of raytracing, spherical texture mapping, overlays, and ambient occlusion to generate beautiful topographic 2D and 3D maps. In addition to maps, rayshader
also allows the user to translate ggplot2
objects into beautiful 3D data visualizations.
The models can be rotated and examined interactively or the camera movement can be scripted to create animations. Scenes can also be rendered using a high-quality pathtracer,
rayrender
. The user can also create a cinematic depth of field post-processing effect to direct the user’s focus to important regions in the figure. The 3D models can also be exported to a 3D-printable format with a built-in STL export function.Demonstration
- Generate a 2D map plot with
ggplot2
- Transform the 2D plot into a 3D plot with
rayshader
Data
Population Data.xlsx

real_estate_master_df.csv

Task 1: Generate a 2D map plot with
ggplot2
library(sp) hkmap = readRDS("HKG_adm1.rds") # geo data of HK map
# Preprocessing map_data = data.frame(id=hkmap$ID_1, Code=hkmap$HASC_1, Eng_name=hkmap$NAME_1) map_data$Code = gsub('HK.', '', as.character(map_data$Code)) map_data = merge(map_data, district_name, by = 'Eng_name') hkmapdf = fortify(hkmap) map_data = merge(hkmapdf, map_data, by="id") map_data = merge(map_data, population, by = "Chi_name") map_data$Population = as.numeric(map_data$Population)
library(ggplot2) # Map map_bg = ggplot(map_data, aes(long, lat, group=group, fill = Population)) + geom_polygon() + # Shape scale_fill_gradient(limits=range(map_data$Population), low="#FFF3B0", high="#E09F3E") + layer(geom="path", stat="identity", position="identity", mapping=aes(x=long, y=lat, group=group, color=I('#FFFFFF'))) + theme(legend.position = "none", axis.line=element_blank(), axis.text.x=element_blank(), axis.title.x=element_blank(), axis.text.y=element_blank(), axis.title.y=element_blank(), axis.ticks=element_blank(), panel.background = element_blank()) # Clean Everything map_bg
# Save as PNG xlim = ggplot_build(map_bg)$layout$panel_scales_x[[1]]$range$range ylim = ggplot_build(map_bg)$layout$panel_scales_y[[1]]$range$range ggsave('map_bg1.png', width = diff(xlim)*100, height = diff(ylim)*100, units = "cm")
Step-by-step explanation:
hkmap = readRDS("HKG_adm1.rds") # geo data of HK map
To plot a map of any regions, you need to have the location data on hand. Please download the rds file from the Github repo first.
library(ggplot2) # Map map_bg = ggplot(map_data, aes(long, lat, group=group, fill = Population)) + geom_polygon() + # Shape scale_fill_gradient(limits=range(map_data$Population), low="#FFF3B0", high="#E09F3E") + layer(geom="path", stat="identity", position="identity", mapping=aes(x=long, y=lat, group=group, color=I('#FFFFFF')))
ggplot2 is another wonderful package in R providing many kinds of charts like bar chart, pie chart, heat map … you name it. In the script shown above, there are 2 main things done. On the line, scale_fill_gradient, the polygons are shown in the map are filled by colour based on population data. Regions with darker colour mean that they have a higher population. Then, layer gives a borderline to each region which is the white line shown.

map_bg + theme(legend.position = "none", axis.line=element_blank(), axis.text.x=element_blank(), axis.title.x=element_blank(), axis.text.y=element_blank(), axis.title.y=element_blank(), axis.ticks=element_blank(), panel.background = element_blank()) # Clean Everything map_bg
The reason for creating this plot is to prepare a background image for the 3D plot that we are going to work out in task 2. Thus, I remove all the gridlines, x-axis and y-axis by using theme.

# Save as PNG xlim = ggplot_build(map_bg)$layout$panel_scales_x[[1]]$range$range ylim = ggplot_build(map_bg)$layout$panel_scales_y[[1]]$range$range ggsave(‘map_bg.png’, width = diff(xlim)*40, height = diff(ylim)*40, units = “cm”)
Lastly, ggsave is used to export the map_bg with the right scale derived from the plot coordinates.
Task 2: Transform the 2D plot into a 3D plot with
rayshader
Data of real_estate_master_df.csv are scraped from Hong Kong Property.
# 2D Plot library(ggplot2) library(grid) estate_price = ggplot(estate_df) + annotation_custom(rasterGrob(hk_map_bg, width=unit(1,"npc"), height=unit(1,"npc")), -Inf, Inf, -Inf, Inf) + xlim(xlim[1],xlim[2]) + # x-axis Mapping ylim(ylim[1],ylim[2]) + # y-axis Mapping geom_point(aes(x=Longitude, y=Latitude, color=apr_price),size=2) + scale_colour_gradient(name = '成交呎價(實)\n(HKD)', limits=range(estate_df$apr_price), low="#FCB9B2", high="#B23A48") + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.title.x=element_blank(), axis.text.y=element_blank(), axis.title.y=element_blank(), axis.ticks=element_blank(), panel.background = element_blank()) # Clean Everything estate_price
Step-by-step explanation:
annotation_custom(rasterGrob(hk_map_bg, width=unit(1,"npc"), height=unit(1,"npc")), -Inf, Inf, -Inf, Inf) + xlim(xlim[1],xlim[2]) + # x-axis Mapping ylim(ylim[1],ylim[2]) + # y-axis Mapping
annotation_custom is used to import the prepared map background image to the plot. xlim(xlim[1],xlim[2]) and ylim(ylim[1],ylim[2]) fix the x- and y-axis of the plot and make them the same as the map_bg.
geom_point(aes(x=Longitude, y=Latitude, color=apr_price),size=2) + scale_colour_gradient(name = '成交呎價(實)\n(HKD)', limits=range(estate_df$apr_price), low="#FCB9B2", high="#B23A48") +
geom_point adds the data points of estate price to the plot.
scale_colour_gradient fills the data points with color based on the variable, “apr_price”, please note that the function used here is different from the one used in task 1. (scale_colour_gradient for geom_point, scale_fill_gradient for geom_polygon)

# 3D Plot library(rayshader) plot_gg(estate_price, multicore = TRUE, width = diff(xlim)*10 ,height=diff(ylim)*10, fov = 70, scale = 300)
# Close Windows rgl.close()
The script to convert the 2D plot to a 3D plot is very simple. The 3D plot is shown in the rgl window. However, remember to close the window after viewing the plot. As the output of the rgl window will not be clear, so the next plot will be overlapped with the previous plot.

Hope you enjoy my article =)
To know more about using rayshader, please go to the official website of rayshader.
Comments
Post a Comment