1 Tutorial

This tutorial explores how different visual variables, color palettes, classification schemes, layers, and formatting choices change the way spatial data are displayed in tmap.

1.1 Visual Variables

1.1.1 Color

The first map uses color to represent county population. Darker counties have larger 2020 populations.

tm_shape(nc_counties) +
  tm_polygons(fill = "POP2020") +
  tm_title("2020 Population by North Carolina County")

1.1.2 Size

Population can also be represented with proportional symbols. Larger symbols represent more people.

tm_shape(nc_counties) +
  tm_borders(col = "gray70") +
  tm_symbols(size = "POP2020", fill = "steelblue", fill_alpha = 0.65) +
  tm_title("County Population Represented by Symbol Size")

1.1.3 Size and Color

This version represents the same variable with both size and color. It makes the most populous counties especially easy to identify.

tm_shape(nc_counties) +
  tm_borders(col = "gray70") +
  tm_symbols(size = "POP2020", fill = "POP2020", fill_alpha = 0.75) +
  tm_title("County Population Represented by Size and Color")

1.1.4 Shape

Shape works best for categorical data. Here, different symbols distinguish school facility types in Durham.

tm_shape(durham_schools) +
  tm_symbols(
    shape = "factype",
    shape.scale = tm_scale(values = 0:9),
    size = 0.7,
    fill = "navy"
  ) +
  tm_title("Durham Schools by Facility Type")

1.2 Color Palettes

cols4all::c4a_gui() opens a palette browser in an interactive R session. It is included below for reference but is not run while knitting because the pop-up can interrupt the render.

cols4all::c4a_gui()

The blue-purple sequential palette is a good match for population because it progresses from lower to higher values.

tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(values = "bu_pu")
  ) +
  tm_title("2020 County Population: Blue-Purple Palette")

1.3 Classification Schemes

The histogram shows that county population is strongly right-skewed. Most counties have relatively small populations, while a few counties are much larger.

ggplot(nc_counties, aes(x = POP2020)) +
  geom_histogram(bins = 20, fill = "#5B8DB8", color = "white") +
  scale_x_continuous(labels = scales::label_comma()) +
  labs(
    title = "Distribution of 2020 County Population",
    x = "Population",
    y = "Number of counties"
  ) +
  theme_minimal()

Equal intervals place counties into classes with the same numeric width. Quantiles place roughly the same number of counties in each class. The quantile map reveals more variation among the many lower-population counties.

map_equal <- tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "equal",
      n = 5
    )
  ) +
  tm_title("Equal Intervals")

map_quantile <- tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "quantile",
      n = 5
    )
  ) +
  tm_title("Quantiles")

tmap_arrange(map_equal, map_quantile, ncol = 2)

1.4 Interactive Mapping

The map below can be panned and zoomed, and counties can be selected for more information.

tmap_mode("view")

tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "quantile",
      n = 5
    ),
    popup.vars = c("County" = "NAME", "2020 population" = "POP2020")
  )

1.5 Multiple Layers

The next map combines a county polygon layer with a point layer showing schools.

durham_county <- nc_counties |>
  filter(NAME == "Durham") |>
  st_transform(st_crs(durham_schools))

tm_shape(durham_county) +
  tm_polygons(fill = "gray95", col = "gray35", lwd = 2) +
  tm_shape(durham_schools) +
  tm_dots(fill = "#C51B7D", size = 0.09, fill_alpha = 0.8) +
  tm_title("Public Schools in Durham County")

1.6 Formatting

1.6.1 Transparency and Additional Classes

This map uses seven quantile classes and partial transparency.

tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill_alpha = 0.55,
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "quantile",
      n = 7
    )
  ) +
  tm_title("2020 Population: Seven Quantile Classes")

1.6.2 Basemap and Title

Adding a labeled basemap provides geographic context, while the title explains what the colors represent.

tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "quantile",
      n = 5
    ),
    fill_alpha = 0.45
  ) +
  tm_title("2020 Population by North Carolina County") +
  tm_basemap("OpenStreetMap")

1.6.3 Layout

The final county map places the legend inside the map, gives it a white background, and adds a rounded frame.

tm_shape(nc_counties) +
  tm_polygons(
    fill = "POP2020",
    fill.scale = tm_scale_intervals(
      values = "bu_pu",
      style = "quantile",
      n = 5
    ),
    fill_alpha = 0.55,
    fill.legend = tm_legend(
      position = tm_pos_in("left", "bottom"),
      frame = TRUE,
      frame.r = 6,
      bg.color = "white",
      item.height = 0.55,
      item.width = 0.55
    )
  ) +
  tm_title("2020 Population by North Carolina County", size = 1.8) +
  tm_basemap("OpenStreetMap") +
  tm_layout(
    text.fontfamily = "serif",
    frame = TRUE,
    frame.r = 15
  )

2 Mini-Challenge

For the mini-challenge, I mapped the percentage of residents living below the poverty line in census tracts across Durham, Orange, and Wake Counties. I used quantiles because poverty rates are not evenly distributed and quantiles make neighborhood-level differences easier to see. The orange-red sequential palette also has an intuitive low-to-high order.

tm_shape(acs_tract_nc) +
  tm_polygons(
    fill = "pct_pov",
    col = "white",
    lwd = 0.5,
    fill.scale = tm_scale_intervals(
      values = "or_rd",
      style = "quantile",
      n = 5
    ),
    fill.legend = tm_legend(
      title = "Below poverty line (%)",
      position = tm_pos_in("left", "bottom"),
      frame = TRUE,
      frame.r = 5,
      bg.color = "white"
    )
  ) +
  tm_title(
    "Poverty Rate by Census Tract\nDurham, Orange, and Wake Counties",
    size = 1.6
  ) +
  tm_layout(
    text.fontfamily = "sans",
    frame = TRUE,
    frame.r = 12
  )

The map shows that poverty is not evenly distributed across the Triangle. Higher-poverty tracts are clustered in particular neighborhoods, while many suburban tracts fall into the lower categories. Because the classes are based on quantiles, the colors show relative differences within these three counties rather than fixed poverty thresholds.