Powered by AppSignal & Oban Pro

Maps: geom_map

notebooks/geom_map.livemd

Maps: geom_map

Mix.install([
  {:giggly, path: Path.join(__DIR__, "..")},
  {:kino, "~> 0.19"}
  ])

About this notebook

This notebook follows the examples on ggplot2's reference page for geom_map(), in the order the page gives them. Each example shows ggplot2's code first and then Giggly's. An example Giggly cannot draw yet keeps its ggplot2 code and names what it is waiting for, so the page is also a list of what remains.

geom_map() draws polygons from a reference map. One table holds the outline of each region and is given as the layer's map; the plot's data holds the values to show for each region; the map_id aesthetic links the two. The map is meant as annotation, so it leaves the position scales alone, and the examples widen them to fit it. geom_map() predates geom_sf() and does not read sf geometry, though it can be drawn with coord_sf(). Giggly has none of this yet.

Polygons from two tables

# First, a made-up example containing a few polygons, to explain
# how `geom_map()` works. It requires two data frames:
# One contains the coordinates of each polygon (`positions`), and is
# provided via the `map` argument. The other contains the
# values associated with each polygon (`values`).  An id
# variable links the two together.

ids <- factor(c("1.1", "2.1", "1.2", "2.2", "1.3", "2.3"))

values <- data.frame(
  id = ids,
  value = c(3, 3.1, 3.1, 3.2, 3.15, 3.5)
)

positions <- data.frame(
  id = rep(ids, each = 4),
  x = c(2, 1, 1.1, 2.2, 1, 0, 0.3, 1.1, 2.2, 1.1, 1.2, 2.5, 1.1, 0.3,
  0.5, 1.2, 2.5, 1.2, 1.3, 2.7, 1.2, 0.5, 0.6, 1.3),
  y = c(-0.5, 0, 1, 0.5, 0, 0.5, 1.5, 1, 0.5, 1, 2.1, 1.7, 1, 1.5,
  2.2, 2.1, 1.7, 2.1, 3.2, 2.8, 2.1, 2.2, 3.3, 3.2)
)

ggplot(values) +
  geom_map(aes(map_id = id), map = positions) +
  expand_limits(positions)

expand_limits(positions) widens the scales to take in the map's coordinates, which geom_map() leaves out of them.

Not in Giggly yet: geom_map() and expand_limits().

Filling each polygon by its value

ggplot(values, aes(fill = value)) +
  geom_map(aes(map_id = id), map = positions) +
  expand_limits(positions)

Not in Giggly yet: geom_map() and expand_limits().

Limiting the y axis

ggplot(values, aes(fill = value)) +
  geom_map(aes(map_id = id), map = positions) +
  expand_limits(positions) + ylim(0, 3)

ylim(0, 3) sets the y axis to run from 0 to 3, a shorter range than the map's.

Not in Giggly yet: geom_map() and expand_limits().

A real map

R's USArrests dataset gives each US state's arrests for murder, assault and rape, and its urban population; the maps package gives the states' outlines. The vars code turns the table long, with one row for each state and column, for the last example.

# Now some examples with real maps
if (require(maps)) {

  crimes <- data.frame(state = tolower(rownames(USArrests)), USArrests)

  # Equivalent to crimes |> tidyr::pivot_longer(Murder:Rape)
  vars <- lapply(names(crimes)[-1], function(j) {
    data.frame(state = crimes$state, variable = j, value = crimes[[j]])
  })
  crimes_long <- do.call("rbind", vars)

  states_map <- map_data("state")

  # without geospatial coordinate system, the resulting plot
  # looks weird
  ggplot(crimes, aes(map_id = state)) +
    geom_map(aes(fill = Murder), map = states_map) +
    expand_limits(x = states_map$long, y = states_map$lat)

Not in Giggly yet: geom_map(), expand_limits(), map data from the maps package, and the USArrests dataset.

A map projection

  # in combination with `coord_sf()` we get an appropriate result
  ggplot(crimes, aes(map_id = state)) +
    geom_map(aes(fill = Murder), map = states_map) +
    # crs = 5070 is a Conus Albers projection for North America,
    #   see: https://epsg.io/5070
    # default_crs = 4326 tells coord_sf() that the input map data
    #   are in longitude-latitude format
    coord_sf(
      crs = 5070, default_crs = 4326,
      xlim = c(-125, -70), ylim = c(25, 52)
    )

Not in Giggly yet: geom_map(), coord_sf(), map data from the maps package, and the USArrests dataset.

A map for each column

 ggplot(crimes_long, aes(map_id = state)) +
   geom_map(aes(fill = value), map = states_map) +
   coord_sf(
     crs = 5070, default_crs = 4326,
     xlim = c(-125, -70), ylim = c(25, 52)
   ) +
   facet_wrap(~variable)
}

Not in Giggly yet: geom_map(), coord_sf(), map data from the maps package, and the USArrests dataset. The facets are in Giggly already, as Giggly.Plot.facet_wrap/3.