Overview
wardboundaries is an R data package for mapping UK ward boundaries.
The main objects in the package are sf tibbles of ward boundaries, obtained from the ONS Open Geography Portal.
- All the original variables and observations are retained and a
geometry
column of type MULTIPOLYGON has been added - LAD codes and names have been added to datasets where required, as I believe they will prove useful in most use cases
The data is ready to be joined to measurements and plotted with e.g. ggplot2
or leaflet
.
Current datasets
20m resolution
- countries_dec2022_20m
- lad_dec2022_20m
- lad_may2023_20m
- lad_dec2023_20m
- wards_dec2022_20m
- wards_may2023_20m
- wards_dec2023_20m
200m resolution
- wards_dec2011
- wards_dec2019
- wards_dec2020
- wards_dec2021
- wards_dec2022
- wards_may2023
- wards_dec2023
500m resolution
- countries_dec2022
- lad_dec2022
- lad_may2023
Use and examples
Access
Accessing a dataset can be done two ways.
By namespace, without loading the whole library
wardboundaries::wards_dec2022
#> Simple feature collection with 8483 features and 12 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -8.649996 ymin: 49.88234 xmax: 1.763571 ymax: 60.86087
#> Geodetic CRS: WGS 84
#> # A tibble: 8,483 × 13
#> WD22CD WD22NM WD22NMW LAD22CD LAD22NM BNG_E BNG_N LONG LAT OBJECTID
#> <chr> <chr> <chr> <chr> <chr> <int> <int> <dbl> <dbl> <int>
#> 1 E05000650 Astley … " " E08000… Bolton 370670 412906 -2.44 53.6 1
#> 2 E05000651 Bradshaw " " E08000… Bolton 374590 413058 -2.39 53.6 2
#> 3 E05000652 Breight… " " E08000… Bolton 374437 409641 -2.39 53.6 3
#> 4 E05000653 Bromley… " " E08000… Bolton 372113 414150 -2.42 53.6 4
#> 5 E05000654 Crompton " " E08000… Bolton 371741 410493 -2.43 53.6 5
#> 6 E05000655 Farnwor… " " E08000… Bolton 373564 406162 -2.40 53.6 6
#> 7 E05000656 Great L… " " E08000… Bolton 371959 408204 -2.42 53.6 7
#> 8 E05000657 Halliwe… " " E08000… Bolton 370828 409676 -2.44 53.6 8
#> 9 E05000658 Harper … " " E08000… Bolton 371512 406080 -2.43 53.6 9
#> 10 E05000659 Heaton … " " E08000… Bolton 367961 409240 -2.49 53.6 10
#> # ℹ 8,473 more rows
#> # ℹ 3 more variables: Shape__Area <dbl>, Shape__Length <dbl>,
#> # geometry <MULTIPOLYGON [°]>
Or by loading the whole library
library(wardboundaries)
wards_dec2022
#> Simple feature collection with 8483 features and 12 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -8.649996 ymin: 49.88234 xmax: 1.763571 ymax: 60.86087
#> Geodetic CRS: WGS 84
#> # A tibble: 8,483 × 13
#> WD22CD WD22NM WD22NMW LAD22CD LAD22NM BNG_E BNG_N LONG LAT OBJECTID
#> <chr> <chr> <chr> <chr> <chr> <int> <int> <dbl> <dbl> <int>
#> 1 E05000650 Astley … " " E08000… Bolton 370670 412906 -2.44 53.6 1
#> 2 E05000651 Bradshaw " " E08000… Bolton 374590 413058 -2.39 53.6 2
#> 3 E05000652 Breight… " " E08000… Bolton 374437 409641 -2.39 53.6 3
#> 4 E05000653 Bromley… " " E08000… Bolton 372113 414150 -2.42 53.6 4
#> 5 E05000654 Crompton " " E08000… Bolton 371741 410493 -2.43 53.6 5
#> 6 E05000655 Farnwor… " " E08000… Bolton 373564 406162 -2.40 53.6 6
#> 7 E05000656 Great L… " " E08000… Bolton 371959 408204 -2.42 53.6 7
#> 8 E05000657 Halliwe… " " E08000… Bolton 370828 409676 -2.44 53.6 8
#> 9 E05000658 Harper … " " E08000… Bolton 371512 406080 -2.43 53.6 9
#> 10 E05000659 Heaton … " " E08000… Bolton 367961 409240 -2.49 53.6 10
#> # ℹ 8,473 more rows
#> # ℹ 3 more variables: Shape__Area <dbl>, Shape__Length <dbl>,
#> # geometry <MULTIPOLYGON [°]>
Map with ggplot2
Perhaps you have some sort of demographic data for each ward. Plotting this as a choropleth with ggplot2 is nice and straightforward
# Make some random numbers that represent a demographic dataset
demo_data <- tibble::tibble(
WD22CD = dplyr::pull(wardboundaries::wards_dec2022, WD22CD),
number_of_ducks = sample(
150:1500, nrow(wardboundaries::wards_dec2022),
replace = TRUE
)
)
demo_data
#> # A tibble: 8,483 × 2
#> WD22CD number_of_ducks
#> <chr> <int>
#> 1 E05000650 1036
#> 2 E05000651 1110
#> 3 E05000652 621
#> 4 E05000653 876
#> 5 E05000654 891
#> 6 E05000655 527
#> 7 E05000656 892
#> 8 E05000657 192
#> 9 E05000658 1031
#> 10 E05000659 968
#> # ℹ 8,473 more rows
# Join the datasets together
ducks_per_ward <- dplyr::left_join(
wardboundaries::wards_dec2022,
demo_data,
by = dplyr::join_by(WD22CD)
)
# Plot
ducks_per_ward |>
ggplot2::ggplot() +
ggplot2::geom_sf(ggplot2::aes(fill = number_of_ducks), colour = NA) +
ggplot2::theme_void()
The following plot isn’t exactly pretty, but it shows how easy it is to focus in on a particular local authority and add labels
# Plot
ducks_per_ward |>
dplyr::filter(LAD22NM == "Vale of White Horse") |>
ggplot2::ggplot() +
ggplot2::geom_sf(
ggplot2::aes(fill = number_of_ducks),
colour = NA,
show.legend = FALSE
) +
ggplot2::geom_sf_label(ggplot2::aes(label = number_of_ducks)) +
ggplot2::theme_void()
Map with leaflet
The following example is mainly cribbed from Leaflet for R. With a couple of format helpers and not much code, it’s possible to create an interactive version of the map
# Format helpers
pal <- leaflet::colorNumeric("Greens", domain = ducks_per_ward$number_of_ducks)
thousands <- scales::label_comma()
labels <- sprintf(
"<strong>%s</strong><br/>%s",
ducks_per_ward$WD22NM, thousands(ducks_per_ward$number_of_ducks)
) |> lapply(htmltools::HTML)
# Leaflet map
leaflet::leaflet() |>
leaflet::addTiles() |>
leaflet::addPolygons(
data = ducks_per_ward,
fillColor = ~ pal(number_of_ducks),
weight = 0.1,
opacity = 0.9,
color = "white",
dashArray = "3",
fillOpacity = 0.9,
highlightOptions = leaflet::highlightOptions(
weight = 1,
color = "yellow",
dashArray = "",
fillOpacity = 0.9,
bringToFront = TRUE
),
label = labels,
labelOptions = leaflet::labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"
)
) |>
leaflet::addLegend(
pal = pal,
values = ducks_per_ward$number_of_ducks,
opacity = 0.9,
title = NULL,
position = "topleft"
)
Further resources
geographr
If your needs are suited to my example maps above then wardboundaries works just fine.
If you have more complex requirements then I recommend checking out geographr. They go far beyond the simple local authority / ward scenario and have boundaries for larger hierarchies, lookup tables, NHS point data, etc. Each dataset in geographr is slimmed down to the essential variables, so you may have to stitch different datasets together. Another important note is that the polygons are highly simplified, whereas wardboundaries offers up to 20m resolution.
Trafford lab API post
A couple of weeks before making this package I knew next to nothing about geospatial data or how to get hold of it. I still don’t, but Trafford Council’s Data lab wrote a very helpful Medium post that explains how to work with the ONS Open Geography Portal’s API to get data in practice. If you have a niche interest this resource will help you understand how to create a specific query for your needs and read the data straight into R with sf
.
R map books
- Spatial Data Science (with applications in R) “introduces and explains the concepts underlying spatial data: points, lines, polygons, rasters, coverages, geometry attributes, data cubes, reference systems, as well as higher-level concepts including how attributes relate to geometries and how this affects analysis”
- Geocomputation with R “is for people who want to analyze, visualize and model geographic data with open source software”
Credits
Developed by Jim Gardner.
Hex image cropped from Invocation 6 by Helvetica Blanc.
Largely modelled on and inspired by geographr.
See LICENCE