19 Day 17: Zones

19.1 Technologies/Techniques

  • More {sf} wrangling
  • Region shapefiles & piracy data from the {asam} package
  • {ggplot2} / geom_sf()

19.2 Data Source: NIMA/NGA Zones & ASAM PIRATE! Data

I pondered a bit on what to do for today’s challenge since there are many types of zones: environmental… temperate… time…

After checking out some of my older blog posts, I went back over to my {asam} package (we used this in a previous challenge) which has a shapefile I made from the National Imagery and Mapping Agency (NIMA)56/National Geospatial-Intelligence Agency (NGA) Catalog of Hydrographic Products.

There are 9 ASAM global regions and 52 ASAM global subregions. These are referenced in REGION and SUBREGION fields in the returned {sf} object. I’m classifying these as zones for the sake of this challenge.

What is an NIMA/ASAM region/subregion? Well, for their use, the world is divided into nine regions: each corresponding to the geographic limits of one of the nine regions in the NIMA catalog. Each region is further divided into numbered subregions. The first digit of all five-digit chart numbers indicates the geographic region while the first two digits of all five-digit chart numbers refer to the geographic subregion. That may be super dry commentary, but at least they’ve got a data strategy.

library(sf)
library(asam)
library(albersusa)
library(rnaturalearth)
library(hrbrthemes)
library(tidyverse)

We’ll grab the NIMA/NGA zones:

zones <- asam_subregions()

and, take a look at the subregions:

plot(zones["SUBREGION"])

Now, we’ll read in pirate data again and turn it into a points geometry object:

if (!file.exists(here::here("data/incidents-17.rds"))) {
  incidents <- read_asam()
  incidents <- st_as_sf(incidents, coords = c("longitude", "latitude"), crs = us_longlat_proj)
  saveRDS(incidents, here::here("data/incidents-17.rds"))
}

incidents <- readRDS(here::here("data/incidents-17.rds"))

glimpse(incidents)
## Observations: 7,843
## Variables: 8
## $ reference   <chr> "2019-73", "2019-72", "2019-75", "2019-74", "2019-76", "2…
## $ date        <date> 2019-09-30, 2019-09-28, 2019-09-23, 2019-09-23, 2019-09-…
## $ navArea     <chr> "XI", "XI", "II", "XI", "II", "XI", "II", "XI", "IV", "II…
## $ subreg      <chr> "71", "92", "57", "72", "51", "71", "51", "71", "26", "57…
## $ hostility   <chr> "Five Armed robbers", "Two High-speed boats", NA, "Seven …
## $ victim      <chr> "Bulk Carrier", "Bulk Carrier", NA, "two fishing vessels"…
## $ description <chr> ", SINGAPORE STRAITS. DECK CREW ON ROUTINE ROUNDS ONBOARD…
## $ geometry    <POINT [°]> POINT (103.65 1.045), POINT (119.7283 5.325), POINT…

Keen eyes will see a subreg field in that tidy {sf} object, so we could just count() by that, but we’lre going to do some work and use st_intersection() to identify the subregion by point.

zones %>%
  left_join(
    st_intersection(incidents, select(zones, SUBREGION)) %>%
      as_tibble() %>%
      count(SUBREGION)
  ) -> locs

glimpse(locs)
## Observations: 58
## Variables: 8
## $ OBJECTID   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ ID         <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ SUBREGION  <int> 83, 19, 17, 18, 21, 28, 27, 11, 26, 25, 12, 13, 14, 15, 16…
## $ REGION     <int> 8, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 9, 4, 7, 2, 2…
## $ Shape_Leng <dbl> 275.90637, 144.08198, 66.67966, 116.55910, 123.27130, 70.8…
## $ Shape_Area <dbl> 4513.12768, 1216.84278, 248.73010, 692.64299, 672.56328, 1…
## $ n          <int> 9, NA, NA, 1, 13, 38, 7, 1, 82, 122, 1, 1, NA, NA, NA, 1, …
## $ geometry   <POLYGON [°]> POLYGON ((-180 -26.98819, -..., POLYGON ((-180 17.…

# or:

left_join(
  mutate(zones, SUBREGION = as.character(SUBREGION)),
  count(incidents, subreg) %>% 
    as_tibble() %>% 
    select(SUBREGION = subreg, n)
) %>% 
  glimpse()
## Observations: 58
## Variables: 8
## $ OBJECTID   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ ID         <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ SUBREGION  <chr> "83", "19", "17", "18", "21", "28", "27", "11", "26", "25"…
## $ REGION     <int> 8, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 9, 4, 7, 2, 2…
## $ Shape_Leng <dbl> 275.90637, 144.08198, 66.67966, 116.55910, 123.27130, 70.8…
## $ Shape_Area <dbl> 4513.12768, 1216.84278, 248.73010, 692.64299, 672.56328, 1…
## $ n          <int> 9, NA, NA, 1, 13, 38, 7, 1, 82, 122, 1, 1, NA, NA, NA, 1, …
## $ geometry   <POLYGON [°]> POLYGON ((-180 -26.98819, -..., POLYGON ((-180 17.…

We’ll also want to plot a normal world map so there’s some idea of where things are, so we’ll grab that from {rnaturalearth} as we’ve done quite a bit:

world <- ne_countries(scale = "medium", returnclass = "sf")

19.3 Drawing the Map

This is just like a choropleth map, but we’re filling zones vs states/countries. To mix things up a bit we’ll also put the counts in the zones at the zone centroid.

ggplot() +
  geom_sf(
    data = world, color = "#2b2b2b", size = 0.125, fill="#d9d9d9"
  ) +
  geom_sf(
    data = select(locs, SUBREGION, n), aes(fill = n),
    size = 0.125, alpha=1/2, color = "#b3000077"
  )  +
  geom_sf_text(
    data = select(locs, SUBREGION, n),
    aes(
      label = I(ifelse(is.na(n), "", scales::comma(n))),
      color = I(ifelse(n > 900, "black", "white")) # change text color depending on the background
    ),
    family = font_es_bold, size = 3.33 # putting a slightly bigger version below with the inverse color
  ) +
  geom_sf_text(
    data = select(locs, SUBREGION, n),
    aes(
      label = I(ifelse(is.na(n), "", scales::comma(n))),
      color = I(ifelse(n > 900, "white", "black")) # change text color depending on the background
    ),
    family = font_es_bold, size = 3.25 # put a slighly smaller version on top with the actual color
  ) +
  scale_fill_viridis_c(
    name = "Number of Anti-Shipping Incidents\n",
    option = "magma", direction = -1, na.value = "white",
    label = scales::comma
  ) +
  labs(
    x = NULL, y = NULL,
    title = "Total Anti-Shipping Activity since 1978 by Sub-Region Zone",
    subtitle = "Zones depicted with red border; counts are total incidents (mostly piracy)",
    caption = "Data source: {asam} • #30DayMapChallenge"
  ) +
  theme_ipsum_es(grid="") +
  theme(legend.position = "top") +
  theme(legend.key.width = unit(3, "lines")) +
  theme(legend.direction = "horizontal")

19.4 In Review

We layered a different shapefile on top of our standard world base shapefile and filled that with values based on computed point-in-polygon frequency.

19.5 Try This At Home

The {mapdeck} package lets you add custom layers like the zones shapefile. Use it to visualize the incidents.

Animate the map over time with a bar chart below it counting incidents by year.