Skip to contents

Using layers: an overview

In Mapbox GL JS and MapLibre, datasets are added to maps as sources which are then styled as layers. mapgl aims to expose the sources and layers APIs to R users in ways that honor the deep customization available in the JavaScript libraries but also accommodate R users’ typical workflows.

Geospatial practitioners in R will typically work with objects from the sf package. The initial release of mapgl natively supports sf objects, and will aim to support other geospatial formats (such as objects from the terra package) in the future.

Objects of class sf can be specified as sources for the map either through the add_source() function or via the source parameter in one of mapgl’s layer functions. The add_fill_layer() function calls the Mapbox GL JS addLayer() function internally with a fill type, and enumerates the available options for styling the layer as function arguments.

mapgl users will often want to use the bounds argument when initializing the map, or alternatively the fit_bounds() function, to fix the map view to a given layer’s bounding box.

library(mapgl)
library(sf)

nc <- st_read(system.file("shape/nc.shp", package="sf"))
## Reading layer `nc' from data source 
##   `/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/sf/shape/nc.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 100 features and 14 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
## Geodetic CRS:  NAD27
mapboxgl(bounds = nc) |> 
  add_fill_layer(id = "nc_data",
                 source = nc,
                 fill_color = "blue",
                 fill_opacity = 0.5)

An overview of available layers in mapgl are below. Layers can be used with either mapboxgl() or maplibre() maps.

Line layers

## To enable caching of data, set `options(tigris_use_cache = TRUE)`
## in your R script or .Rprofile.
options(tigris_use_cache = TRUE)

loving_roads <- roads("TX", "Loving")

maplibre(style = maptiler_style("backdrop"),
         bounds = loving_roads) |>
  add_line_layer(
    id = "roads",
    source = loving_roads,
    line_color = "navy",
    line_opacity = 0.7
  )

Circle layers

Circle layers are typically used to represent point data on your map. Circle clustering is implemented with the cluster_options argument, to which a list generated by the cluster_options() function can be passed.

library(mapgl)
library(sf)
library(dplyr)

# Set seed for reproducibility
set.seed(1234)

# Define the bounding box for Washington DC (approximately)
bbox <- st_bbox(c(
  xmin = -77.119759,
  ymin = 38.791645,
  xmax = -76.909393,
  ymax = 38.995548
),
crs = st_crs(4326))

# Generate 30 random points within the bounding box
random_points <- st_as_sf(
  data.frame(
    id = 1:30,
    lon = runif(30, bbox["xmin"], bbox["xmax"]),
    lat = runif(30, bbox["ymin"], bbox["ymax"])
  ),
  coords = c("lon", "lat"),
  crs = 4326
)

# Assign random categories
categories <- c('music', 'bar', 'theatre', 'bicycle')
random_points <- random_points %>%
  mutate(category = sample(categories, n(), replace = TRUE))

# Map with circle layer
mapboxgl(style = mapbox_style("dark"),
         bounds = random_points) %>%
  add_circle_layer(
    id = "poi-layer",
    source = random_points,
    circle_color = match_expr(
      "category",
      values = c("music", "bar", "theatre",
                 "bicycle"),
      stops = c("#1f78b4", "#33a02c",
                "#e31a1c", "#ff7f00")
    ),
    circle_radius = 8,
    circle_stroke_color = "#ffffff",
    circle_stroke_width = 2,
    circle_opacity = 0.8,
    tooltip = "category",
    hover_options = list(circle_radius = 12,
                         circle_color = "#ffff99")
  ) %>%
  add_categorical_legend(
    legend_title = "Points of Interest",
    values = c("Music", "Bar", "Theatre", "Bicycle"),
    colors = c("#1f78b4", "#33a02c", "#e31a1c", "#ff7f00"),
    circular_patches = TRUE
  )

Symbol layers

Symbol layers offer a wide range of arguments for customizing icon and label appearance; however not all arguments will work with all icons. The icon_image argument will look for a string that represents an icon found in the map style’s sprite. Read more about sprites here.

mapboxgl(style = mapbox_style("light"),
         bounds = random_points) |>
  add_symbol_layer(
    id = "points-of-interest",
    source = random_points,
    icon_image = get_column("category"),
    icon_allow_overlap = TRUE,
    tooltip = "category"
  )

Heatmap layers

Heatmap layers take an object of geometry type POINT and visualize the density of those points in a visually attractive way. add_heatmap_layer() takes sf POINT objects; the example below shows how to read in a remote GeoJSON file as a source as well.

library(mapgl)

mapboxgl(style = mapbox_style("dark"),
         center = c(-120, 50),
         zoom = 2) |>
  add_heatmap_layer(
    id = "earthquakes-heat",
    source = list(
      type = "geojson",
      data = "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
    ),
    heatmap_weight = interpolate(
      column = "mag",
      values = c(0, 6),
      stops = c(0, 1)
    ),
    heatmap_intensity = interpolate(
      property = "zoom",
      values = c(0, 9),
      stops = c(1, 3)
    ),
    heatmap_color = interpolate(
      property = "heatmap-density",
      values = seq(0, 1, 0.2),
      stops = c('rgba(33,102,172,0)', 'rgb(103,169,207)',
                'rgb(209,229,240)', 'rgb(253,219,199)',
                'rgb(239,138,98)', 'rgb(178,24,43)')
    ),
    heatmap_opacity = 0.7
  )

Fill-extrusion layers

library(mapgl)

maplibre(
  style = maptiler_style("basic"),
  center = c(-74.0066, 40.7135),
  zoom = 15.5,
  pitch = 45,
  bearing = -17.6
) |>
  add_vector_source(
    id = "openmaptiles",
    url = paste0("https://api.maptiler.com/tiles/v3/tiles.json?key=",
                 Sys.getenv("MAPTILER_API_KEY"))
  ) |>
  add_fill_extrusion_layer(
    id = "3d-buildings",
    source = 'openmaptiles',
    source_layer = 'building',
    fill_extrusion_color = interpolate(
      column = 'render_height',
      values = c(0, 200, 400),
      stops = c('lightgray', 'royalblue', 'lightblue')
    ),
    fill_extrusion_height = list(
      'interpolate',
      list('linear'),
      list('zoom'),
      15,
      0,
      16,
      list('get', 'render_height')
    )
  )

Raster layers

mapgl supports rasters from the terra package when passed to the data argument in the add_image_source() function, then visualized with add_raster_layer(). Remote raster sources (as shown below) can also be added through add_image_source() for remotely-hosted image files, or add_raster_source() for remotely-hosted raster tiles.

mapboxgl(style = mapbox_style("dark"),
         zoom = 5,
         center = c(-75.789, 41.874)) |>
  add_image_source(
    id = "radar",
    url = "https://docs.mapbox.com/mapbox-gl-js/assets/radar.gif",
    coordinates = list(
      c(-80.425, 46.437),
      c(-71.516, 46.437),
      c(-71.516, 37.936),
      c(-80.425, 37.936)
    )
  ) |>
  add_raster_layer(
    id = 'radar-layer',
    source = 'radar',
    raster_fade_duration = 0
  )

Markers

Markers represent a unique visual component in Mapbox GL JS and MapLibre GL JS, as they highlight locations but do not count as map layers. In mapgl, users can add markers using the add_markers() function. A single marker can be added as a length-2 vector of longitude and latitude; a list of length-2 vectors or an sf POINT object will add multiple markers.

mapboxgl(
  style = mapbox_style("streets"),
  center = c(-74.006, 40.7128),
  zoom = 10
) |> 
add_markers(
  c(-74.006, 40.7128),
  color = "blue",
  rotation = 45,
  popup = "A marker"
)