Introducing 3D ggplots with Rayshader (R) | by Carrie Lo | Towards Data Science

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 repo. Feel free to download and play around.
rayshader
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.
Generate a 2D map plot with ggplot2Transform the 2D plot into a 3D plot with rayshader
Generate a 2D map plot with ggplot2
Transform the 2D plot into a 3D plot with rayshader
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# Preprocessingmap_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)# Mapmap_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 Everythingmap_bg# Save as PNGxlim = ggplot_build(map_bg)$layout$panel_scales_x[[1]]$range$rangeylim = ggplot_build(map_bg)$layout$panel_scales_y[[1]]$range$rangeggsave('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)# Mapmap_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 shown in the map are filled by colour based on population data. Regions with darker colour means 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 Everythingmap_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 PNGxlim = ggplot_build(map_bg)$layout$panel_scales_x[[1]]$range$rangeylim = ggplot_build(map_bg)$layout$panel_scales_y[[1]]$range$rangeggsave(‘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 Plotlibrary(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 Everythingestate_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 Plotlibrary(rayshader)plot_gg(estate_price, multicore = TRUE, width = diff(xlim)*10 ,height=diff(ylim)*10, fov = 70, scale = 300)# Close Windowsrgl.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 erased, 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.
If you find my article useful, please endorse my skills on my linkedIn page to encourage me to write more articles.
Originally published on cydalytics.blogspot.com