A City of Strays: Pie Charts of Animal Rescues in Long Beach
Mapping Long Beach animal rescues with pie charts at district centroids! Used {ggmap} for Stadia Maps, {terra} for rasters, {sf} for spatial data, {scatterpie} for pies, and {ggplot2} to plot.
#TidyTuesday
Donut Chart
{scatterpie}
{ggmap}
Raster Maps
Geocomputation
Author
Aditya Dahiya
Published
March 7, 2025
The Long Beach Animal Shelter Data provides a comprehensive dataset detailing the intake and outcome records of animals at the City of Long Beach Animal Care Services, made accessible through the {animalshelter} R package. Curated by Lydia Gibson for the TidyTuesday challenge on March 4, 2025, this dataset allows enthusiasts to explore trends such as how pet adoptions have evolved over time and which types of pets—ranging from cats and dogs to other species—are most frequently adopted.
Using the Long Beach Animal Shelter dataset, this graphic maps the locations of rescued animals across the city’s districts, leveraging latitude and longitude data. A base map of Long Beach was sourced from {ggmap} and Stadia Maps, converted to a SpatRaster with {terra}, and cropped to district boundaries for precision. Pie charts, created with {scatterpie} and positioned at district centroids using {sf}, illustrate the distribution of rescued animal types—“Cat,” “Dog,” “Bird,” “Wild,” and “Others”—within each district. The analysis reveals that cats are the most frequently rescued animals across all districts, followed by dogs. Notably, Districts 3 and 4 stand out with significant rescues of birds and wild animals alongside cats and dogs, highlighting distinct regional patterns in animal shelter intakes.
Figure 1: This graphic maps animal rescues across Long Beach districts, with pie charts at district centroids showing the prevalence of cats, dogs, birds, wild animals, and others, revealing cats as the most rescued in all areas and Districts 3 and 4 with notable bird and wild rescues. Created using R with {ggmap} (base map from Stadia Maps), {terra} (raster handling), {sf} (spatial data), {scatterpie} (pie charts), and {ggplot2} (plotting).
How I made this graphic?
Loading required libraries
Code
# Data Import and Wrangling Toolslibrary(tidyverse) # All things tidy# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text support for ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # Lighten and Darken colours# Geocomputationlibrary(sf) # Simple features / maps in Rlibrary(osmdata) # Getting Open Street Maps datalibrary(ggmap) # Get background mapslibrary(terra) # Handling rasters in Rlibrary(tidyterra) # Plotting rasters with ggplot2longbeach <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-03-04/longbeach.csv')
Visualization Parameters
Code
# Font for titlesfont_add_google("Encode Sans Condensed",family ="title_font") # Font for the captionfont_add_google("Saira Extra Condensed",family ="caption_font") # Font for plot textfont_add_google("Dosis",family ="body_font") # Font for background textfont_add_google("Sigmar One",family ="back_font")showtext_auto()mypal <-c("#6388B4", "#FFAE34", "#EF6F6A", "#8CC2CA", "#55AD89")# cols4all::c4a_gui()# A base Colourbg_col <-"white"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-"grey30"seecolor::print_color(text_hil)# Colour for the texttext_col <-"grey30"seecolor::print_color(text_col)# Define Base Text Sizebts <-90# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""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_caption <-paste0("**Data:** Lydia Gibson & City of Long Beach Animal Care Services", " | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)# Add text to plot-------------------------------------------------plot_title <-"Paws, Wings, and Tails!\nMapping Animal Rescues in Long Beach"plot_subtitle <-str_wrap("Cats dominate animal rescues across all Long Beach districts, followed by dogs, with Districts 3 and 4 uniquely showing notable numbers of birds and wild animals. This map visualizes shelter intakes using pie charts at district centroids, built with R packages {ggmap}, {terra}, {sf}, {scatterpie}, and {ggplot2}.", 110)str_view(plot_subtitle)
Exploratory Data Analysis and Wrangling
Get City of Long Beach district boundaries from official website.
Code
# Try {geodata} to get district boundaries# longbeach_counties <- geodata::gadm(# country = "USA",# path = tempdir()# )# Trying {tigris} to get districts boundaries # library(tigris)# temp1 <- school_districts(# year = 2020, # state = "CA",# filter_by = longbeach_bb# )# Try {osmdata} to get district boundarieslongbeach_bb <- osmdata::getbb("Long Beach")# # longbeach_districts <- opq(bbox = longbeach_bb) |> # add_osm_feature(# key = "boundary",# value = c("administrative")# ) |> # osmdata_sf()library(httr)# Define the URLshapefile_url <-"https://data.longbeach.gov/api/explore/v2.1/catalog/datasets/colb-council-districts/exports/shp?lang=en&timezone=Asia%2FKolkata"# Create a temporary directorytemp_dir <-tempdir()zip_file <-file.path(tempdir(), "shapefile.zip")# Download the ZIP fileGET(shapefile_url, write_disk(zip_file, overwrite =TRUE))# Unzip the downloaded fileunzip(zip_file, exdir = temp_dir)# Identify the .shp file (assuming it is named 'colb-council-districts.shp')# Identify the .shp file (assuming it is named 'colb-council-districts.shp')shp_file <-file.path(temp_dir, "colb-council-districts.shp")# Read the shapefile into an sf objectcouncil_districts <-st_read(shp_file)rm(temp_dir, shapefile_url, shp_file, zip_file)longbeach_districts <- council_districts |>mutate(district = council_num,pop = population,area = shape_area,geometry = geometry,.keep ="none" )
Code
# library(summarytools)# longbeach |> # dfSummary() |> # view()# # longbeach |> names()# gEt only few types of animals which we can plotdf1 <- longbeach |>mutate(animal_type =case_when( animal_type =="cat"~"Cat", animal_type =="dog"~"Dog", animal_type =="bird"~"Bird", animal_type =="wild"~"Wild",.default ="Others" ),animal_type =fct( animal_type,levels =c("Cat", "Dog", "Bird", "Wild", "Others" )) ) |>st_as_sf(coords =c("longitude", "latitude"),crs ="EPSG:4326" ) |>select(animal_type, geometry) |>st_join(longbeach_districts)# Animal Type per districtdf2 <- df1 |>st_drop_geometry() |>group_by(district) |>count(animal_type) |>filter(!is.na(district))# A comparison chartdf2 |>ggplot(aes(x = district, y = n, fill = animal_type ) ) +geom_col(position ="fill" )# Convert df2 into a tibble that {scatterpie} can understanddf3 <- df2 |>pivot_wider(id_cols = district,names_from = animal_type,values_from = n ) |>mutate(total = Cat + Dog + Bird + Wild + Others ) |># Add latitude and longitude from centroids of districtsleft_join( council_districts |>mutate(district = council_num) |>select(district, geometry) |>st_centroid() |>mutate(longitude =st_coordinates(geometry)[,1],latitude =st_coordinates(geometry)[,2] ) |>st_drop_geometry() )
Code
# Required packageslibrary(ggmap)library(terra)library(ggplot2)library(tidyterra)# Get the map from ggmap# register_stadiamaps("YOUR-API-KEY-HERE")# Define the bounding box (assuming longbeach_bb is defined elsewhere)# Example: longbeach_bb <- c(left = -118.25, bottom = 33.75, right = -118.10, top = 33.85)longbeach_basemap <-get_stadiamap(bbox = longbeach_bb,zoom =15,maptype ="stamen_toner_lite")# Create a SpatRaster from the {ggmap} base mapspat_raster1 <-rast(longbeach_basemap) |>crop(vect( council_districts |>st_geometry() |>st_union() ),mask =TRUE )
# Saving a thumbnaillibrary(magick)# Saving a thumbnail for the webpageimage_read(here::here("data_vizs", "tidy_animal_shelter.png")) |>image_resize(geometry ="x400") |>image_write( here::here("data_vizs", "thumbnails", "tidy_animal_shelter.png" ) )
Session Info
Code
# Data Import and Wrangling Toolslibrary(tidyverse) # All things tidy# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text support for ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # Lighten and Darken colours# Geocomputationlibrary(sf) # Simple features / maps in Rlibrary(osmdata) # Getting Open Street Maps datalibrary(ggmap) # Get background mapslibrary(terra) # Handling rasters in Rlibrary(tidyterra) # Plotting rasters with ggplot2sessioninfo::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