Interactive Maps with tmap and Shiny

Session 4: Layer groups and controls

Martijn Tennekes

Layer groups

What are layer groups?

In view mode, the interactive map has a layer control box in the corner.

  • Each layer can be assigned to a named group
  • Users can toggle groups on and off using check boxes or radio buttons
  • Basemaps are always radio buttons (only one at a time)
  • Data layers default to check boxes

This is set via the group and group.control arguments in layer functions.

The group argument

tmap_mode("view")

tm_shape(NLD_dist) +
  tm_polygons(
    fill = "dwelling_value",
    col = NULL,
    fill.legend = tm_legend("Dwelling value (×1000)"),
    group = "District data") +          # ← group name
tm_shape(NLD_muni) +
  tm_borders(lwd = 1, group = "Municipality borders") +
tm_shape(NLD_prov) +
  tm_borders(lwd = 3)                   # ← group name inherited from tm_shape()

If group is not specified, the group name is inherited from the shape name in tm_shape().

group.control options

Value Behaviour
"check" Check box — layer can be shown/hidden independently (default)
"radio" Radio button — only one layer in the radio group shown at a time
"none" Layer is always visible; not shown in the control box

Switching between layers with radio buttons

Useful when the same variable is available at different spatial resolutions. group.control = "none" on the basemap keeps it out of the radio group:

tmap_mode("view")

tm_shape(NLD_dist) +
  tm_polygons(fill = "dwelling_value", col = NULL,
    fill.legend = tm_legend("Dwelling value (×1000)"),
    group = "District level", group.control = "radio") +
tm_shape(NLD_muni) +
  tm_polygons(fill = "dwelling_value", col = NULL,
    fill.legend = tm_legend("Dwelling value (×1000)"),
    group = "Municipality level", group.control = "radio") +
tm_shape(NLD_prov) +
  tm_borders(lwd = 3, group.control = "none") +
tm_basemap("OpenStreetMap", group.control = "none")

Layer draw order

The plotting order of layers is determined by the zindex argument (or call order when not specified) — not by the group.

# Explicit z-ordering: higher zindex = drawn on top
tm_shape(NLD_dist) +
  tm_polygons("dwelling_value", zindex = 1) +
tm_shape(NLD_muni) +
  tm_borders(zindex = 2)

tm_basemap() is always drawn at the bottom regardless of zindex.

Groups and basemaps together

A simple complete example combining layer groups and multiple basemaps:

tmap_mode("view")

tm_shape(NLD_dist) +
  tm_polygons(fill = "dwelling_value", col = NULL,
    group = "Dwelling values") +
tm_shape(NLD_muni) +
  tm_borders(lwd = 1, group = "Municipality borders") +
tm_basemap(c("CartoDB.Positron",
             "OpenStreetMap",
             "Esri.WorldImagery"))

Advanced grouping

Zoom-specific layers with tm_group()

tm_group() controls which zoom levels a layer group is visible at:

tmap_mode("view")
tm_shape(NLD_dist) +
  tm_polygons(
    fill = "dwelling_value",
    fill.scale = tm_scale_intervals(values = "-brewer.rd_yl_bu",
      breaks = c(75, 150, 250, 500, 750, 1000, 1600)),
    fill.legend = tm_legend("Dwelling value (x 1000)"),
    group = "District") +
tm_shape(NLD_muni) +
  tm_polygons(
    fill = "dwelling_value",
    fill.scale = tm_scale_intervals(values = "-brewer.rd_yl_bu",
      breaks = c(75, 150, 250, 500, 750, 1000, 1600)),
    fill.legend = tm_legend_hide(),
    group = "Municipality") +
  tm_borders(group = "District", lwd = 2) +
tm_title("Zoom in for district level (and out for municipality level)") +
tm_shape(NLD_prov) +
  tm_borders(lwd = 3, group.control = "none") +
tm_group("Municipality", zoom_levels = 8:10) +
tm_group("District",     zoom_levels = 11:14) +
tm_view(set_zoom_limits = c(8, 14))

Controlling zoom limits with tm_view()

set_zoom_limits sets the minimum and maximum zoom the user can reach. This pairs naturally with zoom-dependent groups:

tm_view(set_zoom_limits = c(8, 14))
# zoom 8  = country level (Netherlands fits the screen)
# zoom 14 = street level

Other useful tm_view() options:

Argument Effect
set_zoom_limits Min and max zoom level
set_view Initial zoom and centre
control.position Where the layer control box appears

See ?tm_view for all options.

Recap

  • Use group = "Name" in any layer function to assign it to a named group
  • group.control = "check" (default) → check box; "radio" → radio button; "none" → always on
  • Draw order is controlled by zindex, not by group assignment
  • tm_group() enables zoom-dependent layer visibility
  • More: layer groups vignette