Mapping terrorist attacks in London: specimen answer

Return to the exercise.

This document walks through a specimen answer to this exercise. Read each code section and its numbered explanations below. Select the number at the start of each explanation to highlight the line of code that explanation relates to.

In R, like in many programming languages, there is usually more than one way to complete a task. This code shows one way to complete this exercise. There are other ways to organise this code that are equally good and achieve the same result, but the code below tries to balance making the code efficient with making it easy to read. If your code is different from the code here, read through the notes below the answer and use those to decide whether you prefer your own approach or the specimen answer below. You can also use the notes to improve your own code.

WarningThe specimen answer expects your computer to be set up as outlined in the book.

The online textbook sets out how to install the software you need for crime mapping with R and set up your computer to store files in particular places. The specimen answer below assumes that you have followed those instructions and that your computer is set up as described in the book. If you have not done so, the code in the specimen answer may not run correctly.

The finished map

<httr2_response>
GET https://mpjashby.github.io/crimemappingdata/london_attacks.csv
Status: 200 OK
Content-Type: text/csv
Body: On disk '/Users/mattashby/Documents/Crime Mapping Book/exercises/data/raw/london_attacks.csv' (602 bytes)
Rows: 8 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): description
dbl  (2): latitude, longitude
date (1): date

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Point map of fatal terrorist attacks in London between 2010 and 2018. Red points mark incident locations on a light street base map.

# Map recorded fatal terrorist attacks in London from 2010 to 2018.

# Load packages
pacman::p_load(here, httr2, sf, sfhotspot, tidyverse)

# Download the original data and keep a local copy
request("https://mpjashby.github.io/crimemappingdata/london_attacks.csv") |>
  req_perform(path = here("data", "raw", "london_attacks.csv"))

# Read data
attacks <- here("data", "raw", "london_attacks.csv") |>
  read_csv()

# Keep only records within the requested date range, including both ends
attacks_filtered <- filter(attacks, date >= "2010-01-01", date <= "2018-12-31")

# Convert longitude and latitude to a spatial object
attacks_sf <- st_as_sf(
  attacks_filtered,
  coords = c("longitude", "latitude"),
  crs = "EPSG:4326"
)

# Transform to a projected coordinate reference system suitable for London
attacks_sf_trans <- st_transform(attacks_sf, crs = "EPSG:27700")

# Display the map
hotspot_map(
  attacks_sf_trans,
  basemap_type = "cartolight",
  colour = "red2",
  size = 5
)

Download the complete R script.

Step 1: Explain the script

1# Map recorded fatal terrorist attacks in London from 2010 to 2018.
1
Start by explaining what the script does. In this case the comment identifies the type of crime, the place and the date range, so that it will be easier to find the script if we want to use it again later. See Organising code with comments.

Step 2: Load packages

# Load packages
2pacman::p_load(here, httr2, sf, sfhotspot, tidyverse)
2
pacman::p_load() loads the packages needed for this analysis, listed in alphabetical order with the tidyverse package last. here allows us to specify where to find files in the workspace; httr2 downloads data from the internet; sf handles spatial objects; sfhotspot creates crime maps and estimates density; tidyverse provides tools for reading, wrangling and plotting data. Loading packages at the start makes it easy to check which tools the script needs. See Loading packages.

Step 3: Download the original data

# Download the original data and keep a local copy
3request("https://mpjashby.github.io/crimemappingdata/london_attacks.csv") |>
4  req_perform(path = here("data", "raw", "london_attacks.csv"))
3
request() specifies the URL of the file to download. The pipe operator (|>) passes that request to the next function. This exercise uses one CSV file containing the locations of fatal terrorist attacks in London. See Downloading and loading data; Stringing functions together.
4
req_perform() performs the download. here() specifies where the original file should be saved, relative to the crime_mapping workspace. Keeping the original data in data/raw means we can return to it if we make a mistake while wrangling the data. See Keeping your files organised.

Step 4: Read the downloaded data

# Read data
5attacks <- here("data", "raw", "london_attacks.csv") |>
6  read_csv()
5
here() specifies the path to the downloaded CSV file within the workspace. The pipe (|>) passes this path to read_csv().
6
read_csv() from readr reads the local CSV file. here() specifies which file to read, while <- stores the result as attacks. Keeping this step separate from filtering makes it easy to inspect the original data if we need to check it. See Loading CSV data; Naming objects.

Step 5: Select the requested dates

# Keep only records within the requested date range, including both ends
7attacks_filtered <- filter(attacks, date >= "2010-01-01", date <= "2018-12-31")
7
filter() from dplyr keeps rows from attacks that satisfy both conditions. >= includes 1 January 2010 and later dates, while <= includes 31 December 2018 and earlier dates. The result is stored as attacks_filtered. We filter by date because the exercise asks for 2010–2018; we do not need a second filter for fatalities because the supplied file already contains fatal attacks. See Filtering rows.

Step 6: Describe the original coordinates

# Convert longitude and latitude to a spatial object
8attacks_sf <- st_as_sf(
9  attacks_filtered,
10  coords = c("longitude", "latitude"),
11  crs = "EPSG:4326"
)
8
st_as_sf() creates a spatial object and stores it as attacks_sf. This tells R that the coordinate columns describe locations on the Earth’s surface. See Converting data to an SF object.
9
Use the filtered records, rather than the original data, so that only attacks within the requested period are mapped.
10
coords names the columns containing the x and y coordinates, in that order. Here the x coordinate is longitude and the y coordinate is latitude.
11
crs = "EPSG:4326" specifies how R should interpret the existing longitude and latitude values. It does not transform those coordinates into a different system.

Step 7: Transform the coordinates

# Transform to a projected coordinate reference system suitable for London
12attacks_sf_trans <- st_transform(attacks_sf, crs = "EPSG:27700")
12
st_transform() converts the coordinates in attacks_sf to the British National Grid, a projected CRS suitable for London. Unlike the previous step, this changes the coordinates themselves. The result is stored as attacks_sf_trans. See Transforming the coordinate reference system.

Step 8: Draw the map

# Display the map
13hotspot_map(
14  attacks_sf_trans,
15  basemap_type = "cartolight",
16  colour = "red2",
17  size = 5
)
13
hotspot_map() plots the spatial data over a base map. Leaving the result unassigned means running this final expression displays the map. Different choices of colour and point size could also produce a good answer. See Drawing the map.
14
Use the transformed spatial object, which contains only the records selected earlier.
15
A light cartolight base map helps readers locate the attacks without distracting from the points. See Base maps.
16
Use red points to give the attacks a strong contrast with the light base map. The points should be more prominent than the streets beneath them. See Visual hierarchy.
17
Set the point size to 5 so that these relatively few incidents can be seen clearly. A size that works for this map might obscure the pattern in a dataset containing thousands of incidents. See Controlling aesthetics.