library(sf)
library(tidyverse)
library(leaflet)
library(htmltools)
This is a walk-through tutorial to get from MetaWards output to a map plot (choropleth). The code presented here can be used to write custom functions for easy plotting of model output.
Note: This section is only for completeness, to run the code simply jump to the next section.
The 2011 census ward boundaries shapefile were be downloaded from
statistics.digitalresources.jisc.ac.uk.
I used the mapshaper command line tool (see https://github.com/mbloch/mapshaper) to reduce the number of points and thus bring the filesize down from 350Mb to about 3Mb. The mapshaper shell commands used were
mapshaper infuse_ward_lyr_2011.shp -simplify visvalingam 0.005 \
-o force infuse_ward_lyr_2011_coarse.shp
mapshaper infuse_ward_lyr_2011_coarse.shp snap snap-interval=100 \
-o force infuse_ward_lyr_2011_coarse.shp
Then I transformed the spatial polygons data frame to a sf object in the proper projection and saved it for later use:
library(rgdal)
wards_sf = readOGR('infuse_ward_lyr_2011', 'infuse_ward_lyr_2011_coarse') %>%
st_as_sf() %>%
st_cast('MULTIPOLYGON') %>%
st_transform('+proj=longlat +datum=WGS84 +no_defs') %>%
rename(WD11CD = geo_code)
save(file='wards_sf.Rdata', list='wards_sf')
Download link: wards_sf.Rdata
load('wards_sf.Rdata')
An interactive map of the raw wards boundary data can be displayed in the browser with
# not run:
leaflet(wards_sf) %>%
addTiles() %>%
addPolygons(col='black', weight=1)
Next we load a MetaWards model output. Here we use the file
wards_trajectory_I.csv.bz2 from the 0i3v0i0x001 run in the
experiment
here.
The outputs in this experiment are timeseries of number of infections for each
ward. We extract only the day and ward* columns and calculate the maximum
number of infections per ward:
# load metawards output
mw_output = read.csv('wards_trajectory_I.csv.bz2')
# transform to tbl and calculate max. number of infections per ward
max_infect =
mw_output %>%
as_tibble %>%
select(day, starts_with('ward')) %>%
gather('ward', 'infect', -day) %>%
group_by(ward) %>%
summarise(max_infect = max(infect))
Next we remove ward[0] and extract the integer index of each ward so we can
later match them with the ward ID from the MetaWards lookup table.
# remove ward 0, extract ward index from col name, and match with ward id
max_infect_ward =
max_infect %>%
filter(ward != 'ward.0.') %>%
mutate(FID = as.numeric(str_extract(ward, '\\d+')))
The MetaWards ward lookup table Ward_Lookup.csv was downloaded from
here.
The first column holds the ward id (WD11CD) which we extract and join it with
maximum infections data frame on column name FID. The WD11CD is also a column
in the wards boundaries data so it will be used to combine the data frames
later.
metawards_lookup = read.csv('Ward_Lookup.csv') %>%
as_tibble %>%
select(WD11CD, FID)
max_infect_ward = left_join(max_infect_ward, metawards_lookup, by='FID')
TODO: chase this up later, but ok for now
setdiff(metawards_lookup$WD11CD, wards_sf$WD11CD)
## [1] "E05003230" "E05003216" "W05000053" "W05000055" "W05000058" "W05000068"
## [7] "W05000080" "W05000081" "W05000082" "W05000083" "W05000084" "W05000088"
## [13] "W05000294" "W05000300" "W05000309" "W05000001" "W05000108" "W05000035"
## [19] "W05000041" "W05000045" "W05000047" "W05000050" "W05000320" "W05000604"
## [25] "W05000954" "W05000958" "W05000896" "E05006972" "E05006554" "E05006566"
## [31] "E05000002" "E05000003" "E05000004" "E05000005" "E05000006" "E05000007"
## [37] "E05000008" "E05000009" "E05000010" "E05000011" "E05000012" "E05000013"
## [43] "E05000014" "E05000016" "E05000017" "E05000018" "E05000019" "E05000020"
## [49] "E05000021" "E05000022" "E05000023" "E05000024" "E05000025" "E05006982"
## [55] "E05008322" "E05008323" "E05008324" "E05008325" "E05008326" "E05005228"
## [61] "E05005246"
Now matching infection data from the model run to the ward boundaries is a
simple merge by WD11CD:
# merge data frames
max_infect_df = merge(wards_sf, max_infect_ward, by='WD11CD')
# ggplot object (13Mb)
plt = ggplot(max_infect_df) +
geom_sf(aes(fill=max_infect), lwd=0) +
scale_fill_gradientn(colours=hcl.colors(n=10))
# show
plt

# to save:
# ggsave('plot.png', plt)
To produce a cool interactive, zoomable map with hover effect, use
leaflet:
# not run:
palette = colorNumeric(palette="viridis", domain=max_infect_df$max_infect)
max_infect_df = max_infect_df %>%
mutate(text = paste(name, max_infect, sep=': '))
map = leaflet(max_infect_df) %>%
addTiles() %>%
addPolygons(fillColor=~palette(max_infect), fillOpacity=0.9,
col='black', weight=2, label = ~htmlEscape(text)) %>%
addLegend(pal=palette, values=~max_infect, opacity=1,
title='Maximum no\n of infections')
# to show map in browser do
# print(map)
# save as html widget
htmlwidgets::saveWidget(map, file='metawards_output_map.html')