Tracing Encounters: Orcas in the Salish Sea

Mapping the paths of Southern Resident killer whale encounters highlights their concentrated movements within key areas of the Salish Sea.

#TidyTuesday
Maps
{ggmap}
Author

Aditya Dahiya

Published

October 16, 2024

This week’s Tidy Tuesday dataset comes from the Center for Whale Research (CWR), which monitors Southern Resident killer whales in the Salish Sea, part of the Pacific Northwest. The dataset, scraped by Jadey Ryan and documented here, contains information on encounters from 2017 to 2024. Each encounter involves photographing and identifying individual whales. The data can be accessed via the {orcas} R package and includes variables like encounter duration, location, and pod. While the dataset is mostly tidy, some inconsistencies such as missing values and negative durations remain. | Source | Data

Background raster images for the map were obtained using StadiaMaps and the R package ggmap (Kahle and Wickham 2013a)

This map visualizes the movements of Southern Resident killer whales, with arrows marking the starting and ending points of each recorded encounter. The concentration of arrows within a small area highlights the key regions in the Salish Sea where these encounters occur most frequently. Background map images provided by StadiaMaps.

How I made this graphic?

Loading required libraries, data import & creating custom functions.

Code
# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy
library(janitor)              # Cleaning names etc.
library(here)                 # Root Directory Management
library(dataverse)            # Getting data from Harvard Dataverse

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Combining plots
library(ggmap)                # To get background map tiles

# Option 1: Loading data directly from GitHub
orcas <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2024/2024-10-15/orcas.csv')

Visualization Parameters

Code
# Font for titles
font_add_google("Lato",
  family = "title_font"
) 

# Font for the caption
font_add_google("Saira Extra Condensed",
  family = "caption_font"
) 

# Font for plot text
font_add_google("Oswald",
  family = "body_font"
) 

showtext_auto()

# Background Colour
bg_col <- "white"

# Colour for the text
text_col <- colorspace::darken("#3d545e", 0.2) 

# Colour for highlighted text
text_hil <- colorspace::darken("#3d545e", 0.2) 

# Define Base Text Size
bts <- 90 

# Caption stuff for the plot
sysfonts::font_add(
  family = "Font Awesome 6 Brands",
  regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf")
)
github <- "&#xf09b"
github_username <- "aditya-dahiya"
xtwitter <- "&#xe61b"
xtwitter_username <- "@adityadahiyaias"
social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username}  </span>")
social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")

plot_title <- "Tracing Encounters: Orcas in the Salish Sea"

plot_caption <- paste0(
  "**Data:** Center for Whale Research (CWR)", 
  " |  **Code:** ", 
  social_caption_1, 
  " |  **Graphics:** ", 
  social_caption_2
  )

subtitle_text <- "Arrows trace the journey of each encounter with a Southern Resident killer whale, offering a visual exploration of where these whales are most frequently observed. The map shows a dense cluster of sightings, underscoring the importance of specific areas in the Salish Sea."
plot_subtitle <- str_wrap(subtitle_text, width = 95)
plot_subtitle |> str_view()

rm(github, github_username, xtwitter, 
   xtwitter_username, social_caption_1, 
   social_caption_2)

Exploratory Data Analysis and Wrangling

Code
# summarytools::dfSummary(orcas) |> 
#   summarytools::view()

# Manually drop the erratic ids by looking at the map
id_to_filter <- c(7, 221, 167, 499, 152, 8, 21, 
                  157, 56, 471, 193, 419, 562)


df <- orcas |> 
  # Only keep relevant variables
  select(
    duration,
    begin_latitude,
    begin_longitude,
    end_latitude,
    end_longitude,
    location
  ) |>
  mutate(
    # remove parenthesis content from duration
    duration = str_remove(duration, "\\s*\\(.*\\)"),
    
    # remove the "s" for seconds
    duration = str_extract(duration, "-?\\d+"),
    
    # convert the duration into number
    duration = as.numeric(duration)
  ) |> 
  
  # remove aberrant durations
  filter(duration >= 0) |> 
  
  filter(
    !(
      (begin_latitude == end_latitude) &
      (begin_longitude == end_longitude)  
    )
  ) |> 
  mutate(
    id = row_number()
  ) |> 
  filter(!(id %in% id_to_filter))

# Data needed to make a Static Graphic with ggmap
library(ggmap)
# Register your Stadia Maps key
stadia_maps_api_key
register_stadiamaps(stadia_maps_api_key, write = FALSE)
 
background_tiles_bbox <- c(
 left = min(c(df$begin_longitude, df$end_longitude), na.rm = T) - 0.2,
 right = max(c(df$begin_longitude, df$end_longitude), na.rm = T) + 0.2,
 top = max(c(df$begin_latitude, df$end_latitude), na.rm = T),
 bottom = min(c(df$begin_latitude, df$end_latitude), na.rm = T) - 0.1
)

background_tiles_bbox <- c(
 left = min(c(df$begin_longitude, df$end_longitude), na.rm = T) - 0.2,
 right = -122.2,
 top = max(c(df$begin_latitude, df$end_latitude), na.rm = T),
 bottom = min(c(df$begin_latitude, df$end_latitude), na.rm = T) - 0.1
)

stamen_tiles_lowres <- ggmap::get_stadiamap(
   background_tiles_bbox,
   zoom = 9,
   maptype = "stamen_terrain_background"
)

# stamen_tiles_10 <- ggmap::get_stadiamap(
#    background_tiles_bbox,
#    zoom = 11,
#    maptype = "stamen_terrain_background"
# )

object.size(stamen_tiles_lowres) |> print(units = "Mb")

The Base Plot (Kahle and Wickham 2013b)

Code
g <- ggmap(
  stamen_tiles_lowres
) +
  geom_segment(
    data = df,
    mapping = aes(
      x = begin_longitude,
      y = begin_latitude,
      xend = end_longitude,
      yend = end_latitude
    ),
    linewidth = 0.5,
    arrow = arrow(
      length = unit(2, "mm")
    ),
    alpha = 0.7,
    linetype = 1,
    colour = "grey20"
  ) +
  labs(
    title = plot_title,
    subtitle = plot_subtitle,
    caption = plot_caption
  ) +
  ggthemes::theme_map(
    base_family = "body_font",
    base_size = bts
  ) +
  # Add a north arrow
  annotation_north_arrow(
    location = "tl",
    style = north_arrow_nautical(text_size = bts, text_col = text_col)
    
  ) + 
  
  theme(
    text = element_text(
      colour = text_hil,
      hjust = 0.5,
      lineheight = 0.3
    ),
    plot.title = element_text(
      margin = margin(10,0,5,0, "mm"),
      hjust = 0.5,
      size = 2 * bts,
      face = "bold"
    ),
    plot.subtitle = element_text(
      margin = margin(5,0,5,0, "mm"),
      hjust = 0.5,
      size = bts
    ),
    plot.caption = element_textbox(
      margin = margin(0,0,5,0, "mm"),
      hjust = 0.5,
      size = 0.5 * bts
    ),
    plot.margin = margin(0,5,0,0, "mm"),
    panel.border = element_blank()
  )

Add-on maps, insets and annotations

Code
g_month <- orcas |> 
  mutate(
    month = month(date, label = TRUE), 
    year = year(date),
    .keep = "used"
  
  ) |> 
  drop_na() |> 
  count(month) |> 
  ggplot(
    mapping = aes(y = month, x = n, label = n)
  ) +
  geom_col(
    colour = "transparent",
    fill = alpha(text_col, 0.5),
    width = 0.75
  ) +
  geom_text(
    hjust = 0,
    nudge_x = 2,
    family = "body_font",
    colour = text_col,
    fontface = "bold",
    size = bts / 6
  ) +
  scale_x_continuous(
    expand = expansion(c(0, 0.05)), 
    breaks = seq(0, 160, 40)
  ) +
  coord_cartesian(clip = "off") +
  labs(
    title = "Encounters by the Month\n(2017-2024)",
    y = NULL,
    x = "Number of Encounters"
  ) +
  theme_minimal(
    base_size = bts / 2,
    base_family = "body_font"
  ) +
  theme(
    panel.grid = element_blank(),
    panel.grid.major.x = element_line(
      colour = alpha(text_col, 0.5),
      linetype = 1,
      linewidth = 0.5
    ),
    text = element_text(
      colour = text_col,
      family = "body_font"
    ),
    plot.title = element_text(
      margin = margin(0,0,0,0, "mm"),
      lineheight = 0.3
    ),
    axis.text = element_text(
      margin = margin(0,0,0,0, "mm"),
      colour = text_col
    ),
    axis.ticks = element_blank(),
    axis.ticks.length = unit(0, "mm")
  )

Compiling Plots with {patchwork}(Pedersen 2024)

Code
g_full <- g +
  
  # Add inset to the plot
  inset_element(
    p = g_month, 
    left = 0, 
    right = 0.2,
    bottom = 0,
    top = 0.5, 
    align_to = "panel"
  ) +
  
  # Basix Plot Annotations
  plot_annotation(
    theme = theme(
      plot.background = element_rect(
        fill = "transparent", 
        colour = "transparent", 
        linewidth = 0
      ),
      panel.background = element_rect(
        fill = "transparent", 
        colour = "transparent", 
        linewidth = 0
      )
    )
  )


ggsave(
  filename = here::here("data_vizs", 
                        "tidy_orcas_encounters.png"),
  plot = g_full,
  width = 400, 
  height = 400,
  units = "mm",
  bg = "#99b3cc"
)

Savings the graphics

Code
# Saving a thumbnail

library(magick)
# Saving a thumbnail for the webpage
image_read(here::here("data_vizs", "tidy_orcas_encounters.png")) |> 
  image_resize(geometry = "400") |> 
  image_write(
    here::here(
      "data_vizs", 
      "thumbnails", 
      "tidy_orcas_encounters.png"
    )
  )

Session Info

Code
# Data Import and Wrangling Tools
library(tidyverse)            # All things tidy
library(here)                 # Root Directory Management

# Final plot tools
library(scales)               # Nice Scales for ggplot2
library(fontawesome)          # Icons display in ggplot2
library(ggtext)               # Markdown text support for ggplot2
library(showtext)             # Display fonts in ggplot2
library(colorspace)           # Lighten and Darken colours
library(patchwork)            # Combining plots
library(ggmap)                # To get background map tiles

sessioninfo::session_info()$packages |> 
  as_tibble() |> 
  select(package, 
         version = loadedversion, 
         date, source) |> 
  arrange(package) |> 
  janitor::clean_names(
    case = "title"
  ) |> 
  gt::gt() |> 
  gt::opt_interactive(
    use_search = TRUE
  ) |> 
  gtExtras::gt_theme_espn()
Table 1: R Packages and their versions used in the creation of this page and graphics

References

Kahle, David, and Hadley Wickham. 2013b. “Ggmap: Spatial Visualization with Ggplot2” 5. https://journal.r-project.org/archive/2013-1/kahle-wickham.pdf.
———. 2013a. “Ggmap: Spatial Visualization with Ggplot2” 5. https://journal.r-project.org/archive/2013-1/kahle-wickham.pdf.
Pedersen, Thomas Lin. 2024. “Patchwork: The Composer of Plots.” https://CRAN.R-project.org/package=patchwork.