--- title: "Introduction to rdeck" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to rdeck} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", out.width = "100%" ) ``` `{rdeck}` lets you use R to describe interactive WebGL maps built with Uber's [deck.gl](https://deck.gl) framework and [mapbox](https://www.mapbox.com/) base maps. These maps can smoothly render data interactively on a much larger scale than html/svg frameworks like Leaflet. Visually appealing 3D and lighting effects are also possible, thanks to WebGL rendering. `{rdeck}` maps consist of a basemap (requires a mapbox account) and 0 or more layers. Basemaps are defined via the `rdeck(map_style)` parameter, which is any valid mapbox style identifier, e.g. `"mapbox://styles/mapbox/dark-v10"`. The basemap can be disabled with `rdeck(map_style = NULL)`. Layers are added to an `{rdeck}` map by calling one of the _add layer_ methods on an rdeck instance. e.g. `rdeck() %>% add_scatterplot_layer()`. The order in which layers are added determines their z-index; each layer added to the map will appear _on top_ of all previously added layers. Every layer can be created with their defaults, however layers without data are almost useless as they will be empty. All layer arguments (other than `rdeck`) must be named. Some examples follow. # Packages These packages are required for the following examples. With exception of `RcppSimdJson`, these packages will be frequently used in creating `{rdeck}` maps. ```{r packages, message=FALSE} library(rdeck) library(dplyr) library(sf) library(viridis) # loading deck.gl example data library(RcppSimdJson) ``` # Loading example data In this example, we create a simple scatterplot layer using the [manhattan example data](https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scatterplot/manhattan.json) from deck.gl. This data can be used directly from the URL, however this is not typical usage of `{rdeck}`, so first we'll load this data into a data frame. ```{r manhattan-data} url <- file.path( "https://raw.githubusercontent.com/visgl/deck.gl-data/master", "examples/scatterplot/manhattan.json", fsep = "/" ) manhattan_data <- fload(url) %>% as_tibble(.name_repair = ~ c("lon", "lat", "species")) %>% mutate( position = sfc_point(lon, lat), species = as.factor(species), species_name = if_else(species == 1, "dog", "cat") ) ``` The manhattan data we have loaded. Our scatterplot layer will make use of the `position` column to define the centre of each rendered point, and the `species` column to change the point colours and sizes. `lon` and `lat` aren't used in any of the layer parameters, so they won't be serialised. ```{r echo=FALSE} manhattan_data ``` # Scatterplot map Here we create a simple scatterplot map with a dark vector basemap and `cividis()` colour scale for the scatterplot layer. The scatterplot points are _scaled_ by the `species` categories found in the data (which in this dataset are `dog` and `cat`); this colour scale generates a categorical legend. Point density is highlighted with `additive` blending, making dense areas appear brighter. All points have the same radius of `30 metres`, with a minimum of `0.5 pixels` (to prevent them from disappearing at low zooms). Hovered points will become brighter --- by using a similar colour scale as `get_fill_color` --- and will render a tooltip containing the `species` category from the data. ```{r scatterplot-map} rdeck( map_style = mapbox_dark(), # set the bounds of the map to include all of the manhattan data initial_bounds = st_bbox(manhattan_data$position), # add a 2 pixel buffer to each point, making it easier to hover picking_radius = 2 ) %>% add_scatterplot_layer( name = "manhattan_animals", data = manhattan_data, # the coloumn in manhattan_data which contains the location of each point get_position = position, # a categorical colour scale, using the species column and a cividis colour palette get_fill_color = scale_color_category( col = species, palette = cividis(2) ), # the radius of each point (default 1 metre) is scaled by 30 radius_scale = 30, radius_min_pixels = 0.5, # highlight dot density blending_mode = "additive", # interactivity pickable = TRUE, auto_highlight = TRUE, # per-species highlight colour highlight_color = scale_color_category( col = species, palette = c("#0060e6", "#fff399"), legend = FALSE ), tooltip = c(species, species_name) ) ``` # Grouping points In the following example, we group all points by `species_name` so that we can highlight all points of a given species at once. ```{r manhattan-data-grouped} manhattan_data_grouped <- manhattan_data %>% group_by(species_name) %>% summarise( position = st_union(position), count = n(), .groups = "drop" ) ``` Our grouped data: ```{r echo=FALSE} manhattan_data_grouped ``` The following map is almost identical to the previous map, we have replaced the `data` parameter, the scales to use the `species_name` and the `tooltip` to include the new column, `count`. ```{r scatterplot-map-grouped} rdeck( map_style = mapbox_dark(), # set the bounds of the map to include all of the manhattan data initial_bounds = st_bbox(manhattan_data_grouped$position), # add a 2 pixel buffer to each point, making it easier to hover picking_radius = 2 ) %>% add_scatterplot_layer( name = "manhattan_animals", data = manhattan_data_grouped, # the coloumn in manhattan_data which contains the location of each point get_position = position, # a categorical colour scale, using the species column and a cividis colour palette get_fill_color = scale_color_category( col = species_name, palette = cividis(2) ), # the radius of each point (default 1 metre) is scaled by 30 radius_scale = 30, radius_min_pixels = 0.5, # highlight dot density blending_mode = "additive", # interactivity pickable = TRUE, auto_highlight = TRUE, # per-species highlight colour highlight_color = scale_color_category( col = species_name, palette = c("#0060e6", "#fff399"), legend = FALSE ), tooltip = everything() ) ``` # Scaling additional parameters We are able to scale many parameters on each layer. In the following example, we additionally scale the radius by the count of `species_name`. ```{r scatterplot-map-grouped-2} rdeck( map_style = mapbox_dark(), # set the bounds of the map to include all of the manhattan data initial_bounds = st_bbox(manhattan_data_grouped$position), # add a 2 pixel buffer to each point, making it easier to hover picking_radius = 2 ) %>% add_scatterplot_layer( name = "manhattan_animals", data = manhattan_data_grouped, # the coloumn in manhattan_data which contains the location of each point get_position = position, # a categorical colour scale, using the species column and a cividis colour palette get_fill_color = scale_color_category( col = species_name, palette = cividis(2) ), # we only have 2 groups, so this scale is equivalent to a categorical scale with the # same parameters get_radius = scale_linear( col = count, range = sqrt(1:2) ), # the radius of each point is scaled by 30 radius_scale = 30, radius_min_pixels = 0.5, # highlight dot density blending_mode = "additive", # interactivity pickable = TRUE, auto_highlight = TRUE, # per-species highlight colour highlight_color = scale_color_category( col = species_name, palette = c("#0060e6", "#fff399"), legend = FALSE ), tooltip = everything() ) ``` # High-level layers This example code is repetitive, copy-pasta that will inevitably result in maintenance problems, as well as increased developer effort. Generally when creating maps, we will encapsulate the creation of one or more layers in a function. Similar to creating functions to encapsulate tidy-methods, the use of curly-curly is needed here for any parameters that accept an `accessor` parameter. The only rule in creating a layer function is that the function takes an rdeck map as a parameter (typically first parameter) and that map must be returned; this makes the function chainable. In the following example, we parameterise the data, fill palette, highlight palette, and _get radius_ (which will require curl-curly) for our original scatterplot layer. ```{r layer-function} add_manhattan_layer <- function(rdeck, manhattan_data, fill_palette, highlight_palette, get_radius) { rdeck %>% add_scatterplot_layer( name = "manhattan_animals", data = manhattan_data, get_position = position, get_fill_color = scale_color_category( col = species_name, palette = fill_palette ), # we need curly-curly for get_radius get_radius = {{ get_radius }}, radius_scale = 30, radius_min_pixels = 0.5, blending_mode = "additive", pickable = TRUE, auto_highlight = TRUE, highlight_color = scale_color_category( col = species_name, palette = highlight_palette, legend = FALSE ), tooltip = everything() ) } ``` Usage of our new function is like adding any other layer, just now that it is opinionated and isn't limited to adding a single layer. ```{r layer-function-usage} rdeck( map_style = mapbox_dark(), # set the bounds of the map to include all of the manhattan data initial_bounds = st_bbox(manhattan_data$position), # add a 2 pixel buffer to each point, making it easier to hover picking_radius = 2 ) %>% add_manhattan_layer( manhattan_data = manhattan_data_grouped, fill_palette = viridis(2, alpha = 0.7), highlight_palette = viridis(2), get_radius = scale_category( col = species_name, # swap the levels order, dogs are now bigger levels = c("cat", "dog"), range = c(1, sqrt(3)) ) ) ```