Skip to contents

This gallery shows several ways to use DonutMap. All locations, category values, and links are simulated for demonstration. They are not observed data.

library(DonutMap)
library(ggplot2)
library(sf)
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE

Shared data

The examples use a small synthetic network of locations in eastern Canada. The background layer is a simple sf polygon created inside the vignette, so the gallery does not depend on an external map download.

locations <- data.frame(
  place = c("Hub A", "Hub B", "Hub C", "Hub D", "Hub E", "Hub F"),
  lon = c(-73.6, -72.9, -71.4, -70.8, -69.4, -67.8),
  lat = c(45.5, 46.2, 46.8, 47.7, 48.5, 49.2)
)

categories <- c("Local", "Regional", "Remote")

donut_data <- merge(
  locations,
  data.frame(category = categories),
  by = NULL
)

donut_data$value <- c(
  42, 32, 18,
  28, 64, 22,
  30, 54, 40,
  26, 45, 58,
  20, 38, 72,
  16, 30, 88
)

donut_data$category <- factor(donut_data$category, levels = categories)

flows <- data.frame(
  from = c(
    "Hub A", "Hub A", "Hub B", "Hub B", "Hub C",
    "Hub C", "Hub D", "Hub E", "Hub F"
  ),
  to = c(
    "Hub B", "Hub C", "Hub C", "Hub D", "Hub D",
    "Hub E", "Hub E", "Hub F", "Hub C"
  ),
  volume = c(120, 85, 95, 60, 130, 75, 90, 50, 70),
  corridor = c(
    "Local", "Regional", "Local", "Remote", "Regional",
    "Remote", "Local", "Remote", "Regional"
  )
)

category_colours <- c(
  Local = "#1b9e77",
  Regional = "#7570b3",
  Remote = "#d95f02"
)

background <- sf::st_as_sfc(
  sf::st_bbox(
    c(xmin = -74.5, ymin = 44.8, xmax = -66.8, ymax = 50.0),
    crs = sf::st_crs(4326)
  )
)

study_area <- sf::st_sf(
  area = "Simulated study area",
  geometry = background
)

Use donut_map() without flows when the goal is to compare compositions at locations.

donut_map(
  donut_data,
  place,
  category,
  value,
  lon = lon,
  lat = lat,
  map = study_area,
  crs = 3347,
  radius_range = c(18000, 52000),
  colours = category_colours
) +
  labs(
    title = "Compositions only",
    fill = "Category"
  ) +
  theme(legend.position = "right")

Supplying flows, from, to, and flow_value draws links between donuts. The flow_group and flow_colours arguments colour the links and arrows by a categorical flow variable.

donut_map(
  donut_data,
  place,
  category,
  value,
  lon = lon,
  lat = lat,
  map = study_area,
  crs = 3347,
  radius_range = c(18000, 52000),
  colours = category_colours,
  flows = flows,
  from = from,
  to = to,
  flow_value = volume,
  flow_group = corridor,
  flow_colours = category_colours,
  flow_curvature = 0.25,
  flow_linewidth_range = c(0.3, 2.4),
  flow_arrow = TRUE
) +
  labs(
    title = "Curved links coloured by corridor",
    fill = "Donut category",
    linewidth = "Volume"
  ) +
  theme(legend.position = "right")

Use flow_curvature = 0 for direct straight links. This is often useful for small networks where curved trajectories would add visual clutter.

donut_map(
  donut_data,
  place,
  category,
  value,
  lon = lon,
  lat = lat,
  map = study_area,
  crs = 3347,
  radius_range = c(18000, 52000),
  colours = category_colours,
  flows = flows,
  from = from,
  to = to,
  flow_value = volume,
  flow_group = corridor,
  flow_colours = category_colours,
  flow_curvature = 0,
  flow_linewidth_range = c(0.3, 2.4),
  flow_arrow = TRUE
) +
  labs(
    title = "Straight links",
    fill = "Donut category",
    linewidth = "Volume"
  ) +
  theme(legend.position = "right")

donut_leaflet() creates a clickable leaflet widget. The flow_min argument keeps only larger flows, which can make dense networks easier to read.

donut_leaflet(
  donut_data,
  place,
  category,
  value,
  lon = lon,
  lat = lat,
  map = study_area,
  radius_range = c(18000, 52000),
  colours = category_colours,
  flows = flows,
  from = from,
  to = to,
  flow_value = volume,
  flow_group = corridor,
  flow_colours = category_colours,
  flow_min = 80,
  flow_weight_range = c(1, 7),
  flow_curvature = 0.25,
  flow_arrow = TRUE,
  flow_arrow_size = 35000,
  flow_opacity = 0.8
)

5. Geometry-first workflow

For more customized maps, compute the sf layers first and then plot or transform them yourself.

donut_layer <- donut_polygons(
  donut_data,
  place,
  category,
  value,
  lon = lon,
  lat = lat,
  crs = 3347,
  radius_range = c(18000, 52000)
)

flow_layer <- flow_lines(
  flows,
  donut_data,
  from,
  to,
  volume,
  place,
  group = corridor,
  lon = lon,
  lat = lat,
  crs = 3347,
  flow_curvature = -0.18,
  flow_n = 50
)

ggplot() +
  geom_sf(
    data = flow_layer,
    aes(linewidth = value, colour = group),
    alpha = 0.7
  ) +
  geom_sf(data = donut_layer, aes(fill = category), colour = "white") +
  scale_colour_manual(values = category_colours) +
  scale_fill_manual(values = category_colours) +
  coord_sf(datum = NA) +
  labs(
    title = "Custom map from sf layers",
    colour = "Flow group",
    fill = "Donut category",
    linewidth = "Volume"
  ) +
  theme_minimal()