# Get started with mapboxer: Mapbox GL JS for R

The goal of mapboxer is to make it easy to create interactive maps using Mapbox GL JS 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: 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: 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: 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: 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: Expressions are pretty powerful. Among other things, they can be used for data-driven-styling or to filter your data.

- Shiny Bindings: 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$<widget_id>_onclick` event to get the properties for a clicked feature.

## 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}}"
      )

## 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:

    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:

    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.

## Basemaps

The `style` parameter passed to `mapboxer()` sets the style of the basemap. By default mapboxer uses a Carto vector style. 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()`:

    mapboxer(style = basemap_raster_style())

To use styles from Mapbox 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:

    Sys.setenv(MAPBOX_API_TOKEN = "<yourSuperSecretToken>")

    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-objects and data frames that contain longitudes and latitudes:

    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:

    mvc_cluster <- motor_vehicle_collisions_nyc %>%
      as_mapbox_source(
        lat = "lat",
        lng = "lng",
        cluster = TRUE,
        clusterMaxZoom = 14,
        clusterRadius = 50
      )

See the Sources API Reference 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 for available types. In most cases it is convenient to use one of the `add_*_layer()` functions:

    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 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:

    popup_template <- "Name: {{name}}</br>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 are provided with the `...` parameter. The position is set with the `pos` parameter, one of `top-left`, `top-right`, `bottom-right`, `bottom-left`:

    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 represented as JSON arrays:

    [expression_name, argument_0, argument_1, ...]

Therefore, in R you must use the `list` structure:

    list(expression_name, argument_0, argument_1, ...)

If all elements are of the same type you can also use a vector:

    expr_get_property <- c("get", "<data-property>")`

A simple expression is to use a data property to style your data:

    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:

    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:

    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 for a tutorial and the Expressions Style Specification for details.

## Shiny Bindings

Use `mapboxerOutput()` and `renderMapboxer()` to integrate mapboxer in a Shiny app:

    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:

    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$<widget_id>_onclick` event to get the properties for a clicked feature.