Skip to contents

Adds an interactive slider to a Mapbox GL or MapLibre GL map that steps through a sequence of values and filters one or more layers by a numeric feature property. Works source-agnostically: GeoJSON, vector tile, and PMTiles layers all compose through the same filter pipeline. Optional play button animates through the steps; "cumulative" mode accumulates features through the range rather than swapping them.

Usage

add_slider_control(
  map,
  layers,
  property = NULL,
  time_unit = NULL,
  values = NULL,
  labels = NULL,
  min = NULL,
  max = NULL,
  step = 1,
  mode = c("sequential", "cumulative", "window"),
  window = NULL,
  presentation = c("compact", "timeline"),
  window_behavior = c("auto", "resizable", "fixed"),
  histogram = FALSE,
  histogram_data = NULL,
  counts = NULL,
  initial_value = NULL,
  paint_property = NULL,
  paint_expressions = NULL,
  paint_properties = NULL,
  play_button = FALSE,
  animation_duration = 1000,
  loop = TRUE,
  title = NULL,
  show_value = TRUE,
  slider_style = NULL,
  width = 280,
  position = "top-left",
  draggable = FALSE,
  background_color = "#ffffffcc",
  text_color = "#404040",
  accent_color = "#4a90e2"
)

Arguments

map

A map object created by mapboxgl() or maplibre(), or a proxy object from mapboxgl_proxy() / maplibre_proxy().

layers

Character vector of layer IDs the slider should affect. Layers need not exist yet — if a matching layer is added later via proxy, the slider's current filter and/or paint expression is applied on its first paint.

property

Name of the numeric feature property to filter on. Optional: if NULL, no filter behavior. At least one of property or paint_property must be supplied.

time_unit

Optional. Only consequential when a target layer is a flowmap (add_flowmap()): ordinary layers filter on the numeric property directly, but flowmap layers need an absolute timestamp range, so in "window" mode the numeric value is converted to a flowmap time range using this unit. One of "seconds", "date", or "year" (the absolute units produced by as_time_property()). "month"/"day" are calendar indices, not instants, and cannot drive a flowmap window.

values

Numeric vector of steps. Required unless min and max are supplied.

labels

Optional character vector of display labels (one per value). When omitted, labels default to as.character(values), except when time_unit is "seconds" or "date": then the numeric values are formatted back into readable timestamps ("%Y-%m-%d %H:%M" in UTC for seconds, "%Y-%m-%d" for dates) so a time-driven slider does not display raw epoch numbers. Pass an explicit vector to localize or change the format.

min, max, step

Numeric range specification used when values is not supplied. values is generated via seq(min, max, by = step).

mode

One of "sequential" (default) — each step shows only features matching that exact value; "cumulative" — shows everything up through the current value; or "window" — shows a moving [start, end] range (the end is the current value). Window mode emits a range filter for ordinary layers and a selectedTimeRange for flowmap layers, making it the mode used to drive temporal flowmaps. Only applies when property is set.

window

Only used when mode = "window". NULL (default) makes a cumulative range [min(values), T]; a positive number makes a sliding window [T - window, T], where window is expressed in the same units as values (e.g. for as_time_property() unit = "date", window = 7 is a 7-day window).

presentation

Visual style of the control. "compact" (default) is a small slider (optionally with a density strip behind it). "timeline" renders a prominent, brushable histogram as the control itself – drag the selected window across the bars, or drag its edges to resize it (as in FlowMapBlue's time control). "timeline" requires histogram data (counts or histogram_data) and implies histogram = TRUE.

window_behavior

Only used when mode = "window". "auto" (default) uses a fixed-duration window when window is supplied and a resizable range otherwise. "fixed" pins the window width to window and moves the whole [end - window, end] band. "resizable" lets the user drag either edge to resize the selected range.

histogram

Logical; if TRUE, draw a density strip behind the slider showing how many features fall at each value (a data-density backdrop, most useful with mode = "window" where the selected band highlights the covered bars). Loads d3 on demand. Supply the bar heights via counts or histogram_data. Default FALSE.

histogram_data

Numeric vector of the raw property values across your features; binned in R to the nearest values step to produce the histogram bar heights. Use this when you have the underlying data to hand. Ignored unless histogram = TRUE.

counts

Numeric vector of pre-computed bar heights, one per values. An alternative to histogram_data when you already have per-step counts. Ignored unless histogram = TRUE.

initial_value

Value to start on. In "window" mode this is the window end value and defaults to the last value; other modes default to the first value.

paint_property

Optional Mapbox paint property the slider should animate, supplied in snake_case to match the rest of the package (e.g. "fill_color", "fill_extrusion_height", "circle_radius"). Kebab-case is also accepted for convenience when copying from Mapbox docs. Shortcut for the single-property case; when set, paint_expressions must also be supplied. For animating more than one paint property together (e.g. height and color), use paint_properties instead.

paint_expressions

List of Mapbox expressions, one per value in values. On each step, the expression at the matching index is applied to each target layer via map.setPaintProperty(). Build expressions with interpolate(), match_expr(), or raw lists.

paint_properties

Optional named list for animating multiple paint properties simultaneously. Shape: list(fill_color = list(<expr_0>, <expr_1>, ...), fill_extrusion_height = list(...)) — names are snake_case (kebab-case also accepted), each value is a list of expressions the same length as values. Cannot be combined with paint_property/paint_expressions. Each property's pre-slider value is captured on mount and restored on removal, independently.

play_button

Logical; include a play/pause button next to the slider. Default FALSE.

animation_duration

Milliseconds to wait between steps while playing. Default 1000.

loop

Logical; when playback reaches the last value, return to the first. Default TRUE.

title

Optional title shown above the slider.

show_value

Logical; show the current label beside the slider. Default TRUE.

slider_style

Optional appearance for the slider: a preset string ("light", "dark", or "auto") or a slider_style() object. When omitted, the legacy width, background_color, text_color, and accent_color arguments are used.

width

Slider container width in pixels. Default 280.

position

One of "top-left", "top-right", "bottom-left", "bottom-right". Default "top-left".

draggable

Logical, whether the slider panel can be dragged to a new position on the map by the user. Default FALSE, keeping it docked at position.

background_color, text_color, accent_color

Styling overrides. Defaults match the package's other controls. For full control of the slider appearance, use slider_style.

Value

The modified map or proxy object, invisibly.

Details

Values commonly represent time (years, months, epoch seconds) but need not — any monotonic numeric sequence works (magnitude thresholds, percentile coverage, depth bins, etc.).

The slider composes with other filter sources rather than replacing them: a layer's initial filter = argument, a subsequent set_filter() call, and an interactive add_legend() can all be active at the same time as the slider, and the layer's resolved filter is the ["all", ...] intersection.

Date and time values

The slider requires numeric values. Because sf objects are serialized to GeoJSON via geojsonsf::sf_geojson(), Date and POSIXct columns become JSON strings, not numbers — so you must pre-coerce your time column to numeric before adding the layer. Use as_time_property() for the common cases (year, month index, epoch seconds, days-since-epoch).

Positioning and collisions

The slider is implemented as a native control and stacks beside other native controls (navigation, scale, geolocate, fullscreen) via Mapbox/MapLibre's built-in positioning. Overlays such as add_legend() and add_layers_control() are absolutely-positioned and do not participate in that flow — placing a slider in the same corner as a legend will overlap it. Choose a different corner or adjust the overlay's margins.

Examples

if (FALSE) { # \dontrun{
library(mapgl)
library(sf)

quakes <- sf::read_sf(
  "https://docs.mapbox.com/mapbox-gl-js/assets/significant-earthquakes-2015.geojson"
)
quakes$month <- as.POSIXlt(quakes$time / 1000, origin = "1970-01-01")$mon

mapboxgl(center = c(-45, 0), zoom = 0.25) |>
  add_circle_layer(
    id = "quakes",
    source = quakes,
    circle_color = "#FCA107",
    circle_radius = 20,
    circle_opacity = 0.75
  ) |>
  add_slider_control(
    layers = "quakes",
    property = "month",
    values = 0:11,
    labels = month.name,
    title = "Significant earthquakes in 2015",
    play_button = TRUE
  )

# --- Paint animation: step through a choropleth's color over years
# Given an sf object with columns pop2020, pop2021, ..., pop2024:
# mapboxgl() |>
#   add_fill_layer(id = "tracts", source = tracts, fill_opacity = 0.85) |>
#   add_slider_control(
#     layers = "tracts",
#     values = 2020:2024,
#     paint_property = "fill_color",
#     paint_expressions = lapply(2020:2024, function(y) {
#       interpolate(
#         column = paste0("pop", y),
#         values = c(0, 5e5, 2e6),
#         stops  = c("#f7f4f9", "#67a9cf", "#02818a")
#       )
#     }),
#     play_button = TRUE
#   )
} # }