This document shows you how to map road collisions interactively using the Leaflet package for R.

STATS19 collision data for Greater London during 2014 were downloaded from Transport for London’s Road Safety website. Ward boundary spatial data files were obtained from the London Datastore and converted from shapefile to geoJSON format in QGIS.


First, let’s load the required packages

library(dplyr) # for manipulating data
library(maptools) # for creating SpatialPointsDataFrames
library(rgdal) # for reading geoJSON ward boundary spatial data
library(leaflet) # for interactive mapping

then download the STATS19 casualty data

casualties <- read.csv("https://www.tfl.gov.uk/cdn/static/cms/documents/2014-gla-data-extract-casualty.csv", header = T)
casualties$X <- NULL
glimpse(casualties)
## Observations: 30785
## Variables:
## $ AREFNO                (fctr) 0114CP00001, 0114CP00002, 0114CP00003, ...
## $ Borough               (fctr) CITY OF LONDON, CITY OF LONDON, CITY OF...
## $ Boro                  (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ Easting               (int) 533540, 532680, 532090, 531770, 533130, ...
## $ Northing              (int) 181230, 181430, 181830, 180950, 180920, ...
## $ CREFNO                (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1...
## $ Casualty.Class        (fctr) 3 Pedestrian, 3 Pedestrian, 3 Pedestria...
## $ Casualty.Sex          (fctr) 1 Male, 1 Male, 1 Male, 1 Male, 1 Male,...
## $ Casualty.Age..Banded. (fctr) 25-59, 25-59, Unknown, 25-59, 25-59, 25...
## $ Casualty.Age          (int) 29, 48, 0, 33, 31, 41, 53, 26, 0, 35, 40...
## $ No..of.Casualties     (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ Casualty.Severity     (fctr) 3 Slight, 3 Slight, 2 Serious, 2 Seriou...
## $ Ped..Location         (fctr) 05 Crossing Road (Not On Xing), 05 Cros...
## $ Ped..Movement         (fctr) 9 Unknown Or Other, 9 Unknown Or Other,...
## $ Mode.of.Travel        (fctr) 1 Pedestrian, 1 Pedestrian, 1 Pedestria...

relabel the values in the “Casualty.Severity”

casualties$Casualty.Severity <- factor(casualties$Casualty.Severity,
                                       levels= c("1 Fatal", "2 Serious", "3 Slight"),
                                       labels= c("Fatal", "Serious", "Slight"))

and “Mode.of.Travel” variables

casualties$Mode.of.Travel <- factor(casualties$Mode.of.Travel,
                                    levels= c("1 Pedestrian", "2 Pedal Cycle", "3 Powered 2 Wheeler", "4 Car",
                                              "5 Taxi", "6 Bus Or Coach", "7 Goods Vehicle", "8 Other Vehicle"),
                                    labels= c("Pedestrian", "Pedal Cycle", "Powered 2 Wheeler", "Car",
                                              "Taxi", "Bus or Coach", "Goods Vehicle", "Other Vehicle"))

Then, check the results

table(casualties$Mode.of.Travel, casualties$Casualty.Severity)
##                    
##                     Fatal Serious Slight
##   Pedestrian           64     715   4834
##   Pedal Cycle          13     419   4714
##   Powered 2 Wheeler    27     499   4707
##   Car                  19     297  11487
##   Taxi                  0      13    628
##   Bus or Coach          0      71   1508
##   Goods Vehicle         2      19    631
##   Other Vehicle         2       7    109

Now subset the casualties by “Casualty.Severity” (e.g. Fatal and Serious) and “Mode.of.Travel” (e.g. Pedal Cycle) and retain only the distinct rows

collisions <- casualties %>%
filter(Casualty.Severity == "Fatal" & Mode.of.Travel == "Pedal Cycle" | Casualty.Severity == "Serious" & Mode.of.Travel == "Pedal Cycle") %>% 
  distinct(AREFNO)

which leave collisions involving fatal or serious injury to a pedal cyclist

table(collisions$Mode.of.Travel, collisions$Casualty.Severity)
##                    
##                     Fatal Serious Slight
##   Pedestrian            0       0      0
##   Pedal Cycle          13     418      0
##   Powered 2 Wheeler     0       0      0
##   Car                   0       0      0
##   Taxi                  0       0      0
##   Bus or Coach          0       0      0
##   Goods Vehicle         0       0      0
##   Other Vehicle         0       0      0

Then download the STATS19 attendant data to add in the “Accident.Date”

attendant <- read.csv("https://tfl.gov.uk/cdn/static/cms/documents/2014-gla-data-extract-attendant.csv", header=T)
attendant$Accident.Date <- as.Date(attendant$Accident.Date, "%d-%b-%y")
attendant <- attendant %>%
  select(AREFNO, Accident.Date)
collisions <- merge(collisions, attendant, by="AREFNO")
rm(attendant)

Then add a variable which is a concatenation of incident details. This will be used as a popup in the final map output.

collisions_desc <- function(row)
  with(as.list(row), paste0(format.Date(Accident.Date, "%d-%b-%y"), ":</b> A ", 
                            tolower(Casualty.Severity), " collision involving a ", tolower(Mode.of.Travel)))
strs <- apply(collisions, 1, collisions_desc)
names(strs) <- NULL
collisions$text <- strs

Now, convert the collision data to a SpatialPointsDataFrame

coords <- SpatialPoints(collisions[,c("Easting","Northing")])
collisions.pts <- SpatialPointsDataFrame(coords,collisions)
proj4string(collisions.pts) <- CRS("+init=epsg:27700")
collisions.pts <- spTransform(collisions.pts, CRS("+init=epsg:4326"))

read the ward boundary spatial data

wards = readOGR("/Users/henrypartridge/Dropbox/R/rpubs/mapping_in_leaflet/wards.geojson", "OGRGeoJSON", verbose = FALSE)
proj4string(collisions.pts) <- proj4string(wards)

and count the number of collisions in each ward.

pointsinpolygon <- over(SpatialPolygons(wards@polygons), SpatialPoints(collisions.pts), returnList = TRUE)
wards$count <- unlist(lapply(pointsinpolygon, length)) 
wards <- wards[wards$count != 0,]

Finally, use the Leaflet package to create an interactive map by setting the classification scheme

pal = colorBin('RdPu', wards$count, bins = 5, pretty = TRUE, na.color = "#808080")

creating a ward popup

wards_popup <- paste0("<strong>Ward:  </strong>", wards$NAME,
                      "<br><strong>Borough:  </strong>", wards$BOROUGH,
                      "<br><strong>Pedal cycle KSIs (2014):  </strong>", wards$count)

a pedal cycle icon

icon <- iconList(freq = makeIcon("/Users/henrypartridge/Dropbox/R/rpubs/mapping_in_leaflet/bicycle.png", iconWidth = 12, iconHeight = 12))

and plotting the results.

leaflet(data = wards) %>% 
  addProviderTiles("CartoDB.Positron") %>% 
  setView(-0.103894, 51.503971, zoom = 13) %>% 
  addPolygons(fillOpacity = 0.7,
    fillColor = ~pal(count), color = "darkgrey", weight = 2, popup = wards_popup) %>%
  addLegend(position = c("bottomright"), pal = pal, values = ~count, opacity = 0.7, title = 'Count of collisions') %>% 
  addMarkers(data = collisions.pts, icon = icon, popup = ~text)


The map shows the ward-level distribution of collisions involving fatal or serious injury to pedal cyclists during 2014. Please note that the count of pedal cycle KSIs can be obtained by clicking on each ward and the date and severity of each collision by clicking a pedal cycle icon.