--- title: "Get started with mapboxer: Mapbox GL JS for R" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{mapboxer} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` The goal of mapboxer is to make it easy to create interactive maps using [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/api) within R. Visualizations can be used at the R console, embedded in R Markdown documents or Shiny apps. This guide covers the basic usage. ## Overview * [Map](https://docs.mapbox.com/mapbox-gl-js/api/map/): The map is the main component of your visualization to which you then add other components like layers, controls or sources. Maps are created with `mapboxer()`. * [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/): Sources state which type of data should be displayed on the map. R objects can be converted to Mapbox sources with `as_mapbox_source()`. With `add_source()` or as first parameter of `mapboxer()` sources can be added to the map so that they can be used accross layers. * [Layers](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/): A layer's style define how a source is displayed on the map. Furthermore, you can apply filters to the data of a source. With `add_layer()` you can add any type of layer to the map but in most cases it is easier to use one of the shortcuts like `add_circle_layer()`. * [Controls](https://docs.mapbox.com/mapbox-gl-js/api/markers/): Controls are used to interact with the map. Besides the standard controls like `NavigationControl` included in Mapbox GL JS, mapboxer provides additional controls. For example, `add_filter_control()` can be used to filter your data on the fly without having to set up a Shiny app. * [Expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/): Expressions are pretty powerful. Among other things, they can be used for data-driven-styling or to filter your data. * [Shiny Bindings](https://shiny.rstudio.com/): With `renderMapboxer()` and `mapboxerOutput()` you can integrate your visualizations in Shiny apps. Furthermore, you can use `mapboxer_proxy()` and `update_mapboxer()` to update an already rendered widget. Observe the `input$_onclick` event to get the properties for a clicked feature. ## Quickstart ```{r quickstart} # Load the library library(mapboxer) # Create a source motor_vehicle_collisions_nyc %>% dplyr::mutate(color = ifelse(injured > 0, "red", "yellow")) %>% as_mapbox_source(lng = "lng", lat = "lat") %>% # Setup a map with the default source above mapboxer( center = c(-73.9165, 40.7114), zoom = 10 ) %>% # Add a navigation control add_navigation_control() %>% # Add a layer styling the data of the default source add_circle_layer( circle_color = c("get", "color"), circle_radius = 3, # Use a mustache template to add popups to the layer popup = "Number of persons injured: {{injured}}" ) ``` ![](pix/motor-vehicle-collisions-nyc.png) ## Map With `mapboxer()` you create a map object. This is the main component of your vizualization. To add components like layers or controls or to modify your map you use the `add_*` and `set_*` functions. The options to configure your map are set via the `...` parameter that allows you to pass on any option described in the [Map API Reference](https://docs.mapbox.com/mapbox-gl-js/api/map/): ```r mapboxer( style = basemaps$Carto$dark_matter, center = c(-73.9165, 40.7114), zoom = 9, minZoom = 8 ) ``` With the optional `source` parameter you can add a default source to the map that will be used by the layers if no source is provided. Therefore, it is easy to integrate `mapboxer()` into your workflow: ```{r, eval = FALSE} motor_vehicle_collisions_nyc %>% dplyr::filter(killed > 0) %>% as_mapbox_source() %>% mapboxer( center = c(-73.9165, 40.7114), zoom = 9 ) %>% add_circle_layer(circle_color = "red") ``` As you can see above mapboxer is designed to use the widely used piping style provided by [magrittr](https://magrittr.tidyverse.org/). ## Basemaps The `style` parameter passed to `mapboxer()` sets the style of the basemap. By default mapboxer uses a [Carto vector style](https://github.com/CartoDB/basemap-styles). It is also possible to use raster tiles or a background color as basemap. Therefore, you can use the helpers `basemap_raster_style()` or `basemap_background_style()`: ```r mapboxer(style = basemap_raster_style()) ``` To use styles from [Mapbox](https://www.mapbox.com/maps) it is recommened that your store your API token in an environment variable called `MAPBOX_API_TOKEN`. If not set globally you can store it as follows: ```r Sys.setenv(MAPBOX_API_TOKEN = "") mapboxer(style = basemaps$Mapbox$satellite_v9) ``` ## Sources Sources state which data the map should display. To show the data on the map you need to bind a source to a layer which contains the styling details like color or width. This makes it possible to style the same source in different ways. The easiest way to create a source from an R data object is to use `as_mapbox_source()`. Supported structures are [sf](https://r-spatial.github.io/sf/index.html)-objects and data frames that contain longitudes and latitudes: ```{r, eval = FALSE} mvc_sf <- motor_vehicle_collisions_nyc %>% sf::st_as_sf(coords = c("lng", "lat"), crs = 4326) mvc_source_from_sf <- mvc_sf %>% as_mapbox_source() mvc_source_from_df <- motor_vehicle_collisions_nyc %>% as_mapbox_source(lng = "lng", lat = "lat") ``` With the `...` parameter you can pass additional options to the source: ```r mvc_cluster <- motor_vehicle_collisions_nyc %>% as_mapbox_source( lat = "lat", lng = "lng", cluster = TRUE, clusterMaxZoom = 14, clusterRadius = 50 ) ``` See the [Sources API Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) for available options for the used source type. Sources are either passed to the `add_*_layer` functions or as first parameter to `mapboxer()` setting it as default source. With `add_source()` you can add a source to the map that you refer to in the layer definition by its ID. ## Layers Layers style the data of the source to which they refer. Optionally you can filter features. Each layer must have a unique ID. If you use the generic function `add_layer()`, the type of the layer is specified by the `type` property. See the [Layers API Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) for available types. In most cases it is convenient to use one of the `add_*_layer()` functions: ```{r, eval = FALSE} mapboxer( center = c(-73.9165, 40.7114), zoom = 9 ) %>% add_circle_layer( source = as_mapbox_source(motor_vehicle_collisions_nyc), circle_color = "red", circle_radius = 5 ) ``` ## Popups and tooltips Usually popups are added to a layer with the `popup` parameter of the `add_*_layer()` functions. Optionally you can also use `add_popups()`. The popup text (HTML) is specified by a [mustache](https://github.com/janl/mustache.js) template in which the tags refer to the properties of the layer's data object. If your data contains the properties `name` and `population`, it could look like this: ```r popup_template <- "Name: {{name}}
Population: {{population}}" ``` With `add_tooltips()` you can add tooltips to a layer in the same way. ## Controls Controls are used to interact with the user. They are displayed as overlays on top of the map. Options of the standard controls described in the [Markers and Controls API Reference](https://docs.mapbox.com/mapbox-gl-js/api/markers/) are provided with the `...` parameter. The position is set with the `pos` parameter, one of `top-left`, `top-right`, `bottom-right`, `bottom-left`: ```{r, eval = FALSE} mapboxer() %>% add_navigation_control( pos = "top-left", # Option passed to the 'NavigationControl' showCompass = FALSE ) %>% add_scale_control( pos = "bottom-left", # Option passed to the 'ScaleControl' unit = "nautical" ) %>% add_text_control( pos = "top-right", text = "mapboxer" ) ``` ## Expressions The value of any layout property, paint property (data-driven-styling) or filter may be specified as an expression. Expressions in Mapbox GL JS use a Lisp-like [syntax](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/#syntax) represented as JSON arrays: ```javascript [expression_name, argument_0, argument_1, ...] ``` Therefore, in R you must use the `list` structure: ```r list(expression_name, argument_0, argument_1, ...) ``` If all elements are of the same type you can also use a vector: ```r expr_get_property <- c("get", "")` ``` A simple expression is to use a data property to style your data: ```{r, eval = FALSE} map <- motor_vehicle_collisions_nyc %>% dplyr::mutate( color = ifelse(injured > 0, "red", "yellow") ) %>% as_mapbox_source() %>% mapboxer( center = c(-73.9165, 40.7114), zoom = 9 ) map %>% add_circle_layer( # Expression to get the color from the data's color property circle_color = c("get", "color") ) ``` You can get the same result for the `circle_color` without modifying the data but using expressions only: ```{r, eval = FALSE} map %>% add_circle_layer( circle_color = list( "case", # 'red' if 'injured > 0' list(">", c("get", "injured"), 0), "red", # Defaults to 'yellow' "yellow" ) ) ``` A filter could look like this: ```{r, eval = FALSE} map %>% add_circle_layer( circle_color = c("get", "color"), # Expression to display only data where 'injured > 1' filter = list(">", "injured", 1) ) ``` See also `add_filter_control()` to modify filter expressions on the fly to update your map without the need of a Shiny app, [Get started with expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/) for a tutorial and the [Expressions Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/) for details. ## Shiny Bindings Use `mapboxerOutput()` and `renderMapboxer()` to integrate mapboxer in a [Shiny](https://shiny.rstudio.com/) app: ```{r, eval = FALSE} library(shiny) library(mapboxer) view <- fluidPage( h1("mapboxer"), mapboxerOutput("map") ) backend <- function(input, output) { output$map <- renderMapboxer({ mapboxer(center = c(9.5, 51.3), zoom = 10) %>% add_navigation_control() %>% add_marker(lng = 9.5, lat = 51.3, popup = "mapboxer") }) } if (interactive()) shinyApp(view, backend) ``` With `mapboxer_proxy()` and `update_mapboxer()` you can update your already rendered map: ```{r, eval = FALSE} LAYER_ID <- "crashes" START_VALUE <- 4 view <- basicPage( sliderInput("slider", "Number of persons injured:", min = 0, max = 7, step = 1, value = START_VALUE), mapboxerOutput("map") ) backend <- function(input, output) { output$map <- renderMapboxer({ mapboxer( center = c(-73.9165, 40.7114), zoom = 9 ) %>% add_circle_layer( source = as_mapbox_source(motor_vehicle_collisions_nyc), circle_color = "red", popup = "{{injured}}", filter = list("==", "injured", START_VALUE), id = LAYER_ID ) }) observeEvent(input$slider, { mapboxer_proxy("map") %>% set_filter(LAYER_ID, list("==", "injured", input$slider)) %>% update_mapboxer() }) } if (interactive()) shinyApp(view, backend) ``` Observe the `input$_onclick` event to get the properties for a clicked feature.