Mapping in R can be quite complicated and there are multiple packages to help you do it. This short tutorial based on x, y, z uses the tmap package to create an interactive map of self-harm rates by ward.

There are 5 steps.

  1. Get the data you want to plot.
  2. Find a shape file.
  3. Do some data processing and linkage.
  4. Plot the map
  5. Make it interactive.

1 Step 1: Setup

For this exercise we will use a number of R packages:

knitr::opts_chunk$set(echo = FALSE, include = FALSE)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
suppressMessages(library(readr))
library(rgdal)
rgdal: version: 1.2-5, (SVN revision 648)
 Geospatial Data Abstraction Library extensions to R successfully loaded
 Loaded GDAL runtime: GDAL 2.1.2, released 2016/10/24
 Path to GDAL shared files: /Users/julianflowers/Library/R/3.3/library/rgdal/gdal
 Loaded PROJ.4 runtime: Rel. 4.9.1, 04 March 2015, [PJ_VERSION: 491]
 Path to PROJ.4 shared files: /Users/julianflowers/Library/R/3.3/library/rgdal/proj
 Linking to sp version: 1.2-4 
library(janitor)
library(tmap)
options(digits = 3)

2 Step 2: Get the data

The latest Mental Health JSNA tool has some indicators at ward level. Currently Fingertips doesn’t offer a faclity to map data at this level so this walk through shows how to create these maps in R.

We’ll download the data, convert it to .csv format and import into R.

testward <- read_csv("~/Downloads/testward.csv", col_types = c("ciccccdcccccccc")) %>%
  janitor::clean_names() %>%
  select(x_indicator, area_code, area_name, time_period, value)
15036 parsing failures.
  row         col               expected actual
30055 Time Period no trailing characters    /14
30056 Time Period no trailing characters    /14
30057 Time Period no trailing characters    /14
30058 Time Period no trailing characters    /14
30059 Time Period no trailing characters    /14
..... ........... ...................... ......
See problems(...) for more details.
testward <- testward %>%
  filter(x_indicator == "Hospital admissions for self-harm: standardised emergency admission ratio (all ages)") 

3 Step 3: Get a relevant shape file

Shape files for ward boundaries can be obtained from PHE’s ESRI server or from the ONS Open geography portal. In this example we’ll use the super generalised ward boundaries for 2015 from the Open Geography portal. We’ll use the rgdal package to read in the shape file as a Spatial Data Frame.

shp <- tempfile() ## create a temp file
download.file('http://geoportal.statistics.gov.uk/datasets/5fb8813978cc4e4892da4b57bcf4491f_3.zip', shp) ## download the file
trying URL 'http://geoportal.statistics.gov.uk/datasets/5fb8813978cc4e4892da4b57bcf4491f_3.zip'
downloaded 2.5 MB
unzip(shp, exdir = getwd()) ## unzip
s <- readOGR(dsn = "Wards_December_2015_Super_Generalised_Clipped_Boundaries_in_Great_Britain.shp", layer = "Wards_December_2015_Super_Generalised_Clipped_Boundaries_in_Great_Britain", verbose = FALSE) ## read in th shape file

4 Data linkage and pre-processing

seng <- subset(s, substr(wd15cd, 1, 1) == "E") ## restrict to England wards
seng@data <- left_join(seng@data, testward, by=c("wd15cd" = "area_code")) ## Join indicator data
joining character vector and factor, coercing into character vector
seng@data$country <- "Eng" ## restrict to England
library(viridis) ## colour palette
palette <- rev(plasma(15)) ## colour scheme
indicator <- stringr::str_wrap(seng@data$x_indicator, width = 30) ## labels
style <- "kmeans" ## categorisation
n <-  10 ## categories
credits <- "Contains ordnance survey data (c) \nCrown copyright and database right 2016"

5 Draw map

t <- tm_shape(seng) +
  tm_fill("value", style = style , n = n,
          palette = palette, title = "Self-harm rate SAR:\n100 = national average", 
          popup.vars = c("x_indicator", "value", "area_name")) +
  tm_layout(paste(indicator, ":", style),
            title.size = 1,
            legend.outside = TRUE, 
            frame = FALSE) +
  tm_scale_bar() +
  tm_compass()+
   tm_credits( credits, size = 0.7, align = "right") 
tmap_mode("plot")
t

6 Make it interactive

tl <- tmap_leaflet(t)
Credits not supported in view mode.Compass not supported in view mode.Scale bar not yet supported in view mode, it will be in the next version.
tl

That’s it. There are a lot more tweaks you can make of course.

LS0tCnRpdGxlOiAiMTAgbWludXRlcyB0byBtYXBwaW5nIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCi0tLQoKTWFwcGluZyBpbiBSIGNhbiBiZSBxdWl0ZSBjb21wbGljYXRlZCBhbmQgdGhlcmUgYXJlIG11bHRpcGxlIHBhY2thZ2VzIHRvIGhlbHAgeW91IGRvIGl0LiBUaGlzIHNob3J0IHR1dG9yaWFsIGJhc2VkIG9uIHgsIHksIHogdXNlcyB0aGUgYHRtYXBgIHBhY2thZ2UgdG8gY3JlYXRlIGFuIGludGVyYWN0aXZlIG1hcCBvZiBzZWxmLWhhcm0gcmF0ZXMgYnkgd2FyZC4KClRoZXJlIGFyZSA1IHN0ZXBzLgoKMS4gR2V0IHRoZSBkYXRhIHlvdSB3YW50IHRvIHBsb3QuCjIuIEZpbmQgYSBzaGFwZSBmaWxlLgozLiBEbyBzb21lIGRhdGEgcHJvY2Vzc2luZyBhbmQgbGlua2FnZS4KNC4gUGxvdCB0aGUgbWFwCjUuIE1ha2UgaXQgaW50ZXJhY3RpdmUuCgojIFN0ZXAgMTogU2V0dXAKCkZvciB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgdXNlIGEgbnVtYmVyIG9mIFIgcGFja2FnZXM6CgoqIGByZ2RhbGAgdG8gbG9hZCBTaGFwZSBmaWxlcwoqIGByZWFkcmAgdG8gbG9hZCBkYXRhCiogYGRwbHlyYCBmb3IgZGF0YSB3cmFuZ2xpbmcKKiBgamFuaXRvcmAgZm9yIGNsZWFuaW5nIHVwIGRhdGEgbGFibGVzCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFKQpgYGAKCmBgYHtyIGxvYWQgbGlicmFyaWVzfQpsaWJyYXJ5KGRwbHlyKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocmVhZHIpKQpsaWJyYXJ5KHJnZGFsKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkodG1hcCkKCm9wdGlvbnMoZGlnaXRzID0gMykKYGBgCgoKCiMgU3RlcCAyOiBHZXQgdGhlIGRhdGEKIApUaGUgbGF0ZXN0IFtNZW50YWwgSGVhbHRoIEpTTkEgdG9vbF0oKSBoYXMgc29tZSBpbmRpY2F0b3JzIGF0IHdhcmQgbGV2ZWwuIEN1cnJlbnRseSBGaW5nZXJ0aXBzIGRvZXNuJ3Qgb2ZmZXIgYSBmYWNsaXR5IHRvIG1hcCBkYXRhIGF0IHRoaXMgbGV2ZWwgc28gdGhpcyB3YWxrIHRocm91Z2ggc2hvd3MgaG93IHRvIGNyZWF0ZSB0aGVzZSBtYXBzIGluIGBSYC4KIApXZSdsbCBbZG93bmxvYWQgdGhlIGRhdGFdKCksIGNvbnZlcnQgaXQgdG8gLmNzdiBmb3JtYXQgYW5kIGltcG9ydCBpbnRvIFIuIAoKYGBge3J9CnRlc3R3YXJkIDwtIHJlYWRfY3N2KCJ+L0Rvd25sb2Fkcy90ZXN0d2FyZC5jc3YiLCBjb2xfdHlwZXMgPSBjKCJjaWNjY2NkY2NjY2NjY2MiKSkgJT4lCiAgamFuaXRvcjo6Y2xlYW5fbmFtZXMoKSAlPiUKICBzZWxlY3QoeF9pbmRpY2F0b3IsIGFyZWFfY29kZSwgYXJlYV9uYW1lLCB0aW1lX3BlcmlvZCwgdmFsdWUpCgp0ZXN0d2FyZCA8LSB0ZXN0d2FyZCAlPiUKICBmaWx0ZXIoeF9pbmRpY2F0b3IgPT0gIkhvc3BpdGFsIGFkbWlzc2lvbnMgZm9yIHNlbGYtaGFybTogc3RhbmRhcmRpc2VkIGVtZXJnZW5jeSBhZG1pc3Npb24gcmF0aW8gKGFsbCBhZ2VzKSIpIAoKCmBgYAoKIyBTdGVwIDM6IEdldCBhIHJlbGV2YW50IHNoYXBlIGZpbGUKClNoYXBlIGZpbGVzIGZvciB3YXJkIGJvdW5kYXJpZXMgY2FuIGJlIG9idGFpbmVkIGZyb20gUEhFJ3MgRVNSSSBzZXJ2ZXIgb3IgZnJvbSB0aGUgT05TIE9wZW4gZ2VvZ3JhcGh5IHBvcnRhbC4gSW4gdGhpcyBleGFtcGxlIHdlJ2xsIHVzZSB0aGUgc3VwZXIgZ2VuZXJhbGlzZWQgd2FyZCBib3VuZGFyaWVzIGZvciAyMDE1IGZyb20gdGhlIE9wZW4gR2VvZ3JhcGh5IHBvcnRhbC4gV2UnbGwgdXNlIHRoZSBgcmdkYWxgIHBhY2thZ2UgdG8gcmVhZCBpbiB0aGUgc2hhcGUgZmlsZSBhcyBhICpTcGF0aWFsIERhdGEgRnJhbWUqLgoKYGBge3IsIGNhY2hlID0gVFJVRX0Kc2hwIDwtIHRlbXBmaWxlKCkgIyMgY3JlYXRlIGEgdGVtcCBmaWxlCgpkb3dubG9hZC5maWxlKCdodHRwOi8vZ2VvcG9ydGFsLnN0YXRpc3RpY3MuZ292LnVrL2RhdGFzZXRzLzVmYjg4MTM5NzhjYzRlNDg5MmRhNGI1N2JjZjQ0OTFmXzMuemlwJywgc2hwKSAjIyBkb3dubG9hZCB0aGUgZmlsZQoKdW56aXAoc2hwLCBleGRpciA9IGdldHdkKCkpICMjIHVuemlwCgpzIDwtIHJlYWRPR1IoZHNuID0gIldhcmRzX0RlY2VtYmVyXzIwMTVfU3VwZXJfR2VuZXJhbGlzZWRfQ2xpcHBlZF9Cb3VuZGFyaWVzX2luX0dyZWF0X0JyaXRhaW4uc2hwIiwgbGF5ZXIgPSAiV2FyZHNfRGVjZW1iZXJfMjAxNV9TdXBlcl9HZW5lcmFsaXNlZF9DbGlwcGVkX0JvdW5kYXJpZXNfaW5fR3JlYXRfQnJpdGFpbiIsIHZlcmJvc2UgPSBGQUxTRSkgIyMgcmVhZCBpbiB0aCBzaGFwZSBmaWxlCgpgYGAKCiMgRGF0YSBsaW5rYWdlIGFuZCBwcmUtcHJvY2Vzc2luZwoKYGBge3J9CgpzZW5nIDwtIHN1YnNldChzLCBzdWJzdHIod2QxNWNkLCAxLCAxKSA9PSAiRSIpICMjIHJlc3RyaWN0IHRvIEVuZ2xhbmQgd2FyZHMKc2VuZ0BkYXRhIDwtIGxlZnRfam9pbihzZW5nQGRhdGEsIHRlc3R3YXJkLCBieT1jKCJ3ZDE1Y2QiID0gImFyZWFfY29kZSIpKSAjIyBKb2luIGluZGljYXRvciBkYXRhCnNlbmdAZGF0YSRjb3VudHJ5IDwtICJFbmciICMjIHJlc3RyaWN0IHRvIEVuZ2xhbmQKCmxpYnJhcnkodmlyaWRpcykgIyMgY29sb3VyIHBhbGV0dGUKCnBhbGV0dGUgPC0gcmV2KHBsYXNtYSgxNSkpICMjIGNvbG91ciBzY2hlbWUKaW5kaWNhdG9yIDwtIHN0cmluZ3I6OnN0cl93cmFwKHNlbmdAZGF0YSR4X2luZGljYXRvciwgd2lkdGggPSAzMCkgIyMgbGFiZWxzCnN0eWxlIDwtICJrbWVhbnMiICMjIGNhdGVnb3Jpc2F0aW9uCm4gPC0gIDEwICMjIGNhdGVnb3JpZXMKY3JlZGl0cyA8LSAiQ29udGFpbnMgb3JkbmFuY2Ugc3VydmV5IGRhdGEgKGMpIFxuQ3Jvd24gY29weXJpZ2h0IGFuZCBkYXRhYmFzZSByaWdodCAyMDE2IgoKCgpgYGAKCiMgRHJhdyBtYXAKCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKCnQgPC0gdG1fc2hhcGUoc2VuZykgKwogIHRtX2ZpbGwoInZhbHVlIiwgc3R5bGUgPSBzdHlsZSAsIG4gPSBuLAogICAgICAgICAgcGFsZXR0ZSA9IHBhbGV0dGUsIHRpdGxlID0gIlNlbGYtaGFybSByYXRlIFNBUjpcbjEwMCA9IG5hdGlvbmFsIGF2ZXJhZ2UiLCAKICAgICAgICAgIHBvcHVwLnZhcnMgPSBjKCJ4X2luZGljYXRvciIsICJ2YWx1ZSIsICJhcmVhX25hbWUiKSkgKwogIHRtX2xheW91dChwYXN0ZShpbmRpY2F0b3IsICI6Iiwgc3R5bGUpLAogICAgICAgICAgICB0aXRsZS5zaXplID0gMSwKICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFLCAKICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSkgKwogIHRtX3NjYWxlX2JhcigpICsKICB0bV9jb21wYXNzKCkrCiAgIHRtX2NyZWRpdHMoIGNyZWRpdHMsIHNpemUgPSAwLjcsIGFsaWduID0gInJpZ2h0IikgCgp0bWFwX21vZGUoInBsb3QiKQoKdApgYGAKCiMgTWFrZSBpdCBpbnRlcmFjdGl2ZQoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CnRsIDwtIHRtYXBfbGVhZmxldCh0KQoKdGwKCgpgYGAKClRoYXQncyBpdC4gVGhlcmUgYXJlIGEgbG90IG1vcmUgdHdlYWtzIHlvdSBjYW4gbWFrZSBvZiBjb3Vyc2Uu