Mapping NYC's 23K Trashcan Locations
The New York City Department of Sanitation (DSNY) - the largest department of its kind in the world - is responsible for the city’s garbage-collection operation. One component of this operation is the regular emptying of 23,000+ street-level trashcans - or as they call them: “litter baskets.”
DSNY offers up their geo-coded litter-basket inventory - refreshed monthly - through the NYC OpenData portal and in today’s post I’ll walk through how to create an interactive, 3D fly-over map of these litter-basket locations with the help of Mapbox’s Mapbox GL JS API in R.
Note: In order to follow along today’s post you will need to create a mapbox account and get yourself an access token, too.
DSNY Litter-Basket Data
New York City-dwellers will recognize these four basket types found in the data:
*high-end comes in several forms.
While each record is geo-coded, this particular data does not include the zip code of the basket location. To include that, I’ll use NYC zip code boundary shape files and the sf
package to get the intersection between basket points and zip code boundaries.
pacman::p_load(here, tidyverse, glue, sf, mapboxer)
## pacman library: http://trinker.github.io/pacman
## Other libraries: RSocrata (to access NYC OpenData)
Zip code boundaries
First we download the shapefile for NYC zip codes and read it in using the sf
package:
## download the file containing NYC's ZIP Code Boundaries
# dl_file <- tempfile()
# download.file(glue("https://data.cityofnewyork.us/download/",
# "i8iw-xf4u/application%2Fzip"), dl_file)
#
# ## unzip the file to the desired path
# unzip(dl_file, exdir=here(data_path, "nyc-zip-shapes", "zips"))
nyc_zips_geom <- st_read(here(data_path, "nyc-zip-shapes", "zips",
"ZIP_CODE_040114.shp"), quiet = TRUE) %>%
select(ZIPCODE, PO_NAME, geometry)
DSNY basket inventory
I use RSocrata
to read in the API resource and then use sf
for the intersection between points and boundaries. Also included is some code for styling the map:
dsny_bin_url <- "https://data.cityofnewyork.us/8znf-7b2c"
bins <- RSocrata::read.socrata(dsny_bin_url) %>%
# 18 Baskets ('bins') have missing coords: BX (10),
# Q (6), BK (1), and SI (1) as of the publish date
filter(!is.na(stateplane_labelx)) %>%
st_as_sf(coords = c("stateplane_labelx", "stateplane_labely"),
crs = st_crs(nyc_zips_geom))
# This intersection will provide us with the ZIP_CODE
# for each bin location tooltip.
zipbins <- st_intersection(
st_make_valid(bins), st_make_valid(nyc_zips_geom)
) %>%
st_drop_geometry() %>%
st_as_sf(wkt = "point")
bname <- c("S" = "Standard Wire", "H" = "High-end",
"R" = "Black Top", "C" = "Compacting")
# DSNY Colors
bin_colorizer <- function(x) {
case_when( x == "S" ~ "#006749", x == "R" ~ "#1F2120",
x == "H" ~ "#2B8CFD", x == "C" ~ "#FBB039",
TRUE ~ "gray")
}
Here’s a look at the variable containing the data to be mapped, zipbins
:
## Rows: 23,280
## Columns: 14
## $ basketid <int> 41260025, 41260009, 41260059, 4126~
## $ baskettype <chr> "S", "S", "H", "S", "H", "S", "S",~
## $ direction <chr> "N", "NW", "SW", "SW", "NW", "SW",~
## $ fid <int> 11871, 11636, 20681, 20679, 20401,~
## $ location_description <chr> "N corner of ROCKAWAY BLVD and 146~
## $ ownertype <chr> "D", "D", "D", "D", "D", "D", "D",~
## $ section <chr> "QE126", "QE126", "QE126", "QE126"~
## $ stateplane_snappedx <dbl> 1042085, 1042804, 1041895, 1041416~
## $ stateplane_snappedy <dbl> 184749.0, 183407.0, 187226.9, 1847~
## $ streetname1 <chr> "ROCKAWAY BLVD", "150 ST", "SUTPHI~
## $ streetname2 <chr> "146 ST", "130 AV", "FOCH BLVD", "~
## $ ZIPCODE <chr> "11436", "11436", "11436", "11436"~
## $ PO_NAME <chr> "Jamaica", "Jamaica", "Jamaica", "~
## $ point <POINT> POINT (-73.79138 40.67369), POIN~
Mapboxer GL JS
The mapboxer
package for R allows one to communicate with the Mapbox GLJS API. While it doesn’t offer easy access to the full functionality of the API, you can still achieve quite a lot with it. Below, I create the map showing all 23K+ litter-basket locations.
my_layer_id <- "baskets" # for live-filtering
# in the next section
basketmap <- zipbins %>%
mutate(basketcolor = bin_colorizer(baskettype),
baskettypename = ifelse(is.na(bname[baskettype]),
"Unknown", bname[baskettype])
) %>%
as_mapbox_source() %>%
mapboxer( # basemap$Carto includes:
style = basemaps$Carto$positron, # dark-matter, voyager, and
center = c(-73.943362, 40.702051), # positron. 6 mapbox styles
zoom = 10, pitch = 40, width = "98%", # are also offered
max.zoom = 15
) %>%
add_navigation_control() %>%
add_circle_layer(
circle_color = c("get", "basketcolor"),
circle_blur = 1,
circle_opacity = .8,
popup = paste0("<p>",
"<div style='color:{{basketcolor}};margin:0 auto;'>",
paste0(rep("▬",11), collapse = ""),
"</div>",
"<b>{{baskettypename}} Basket</b><br>",
"{{PO_NAME}}, {{ZIPCODE}}<br>",
"Cross of: ",
"{{streetname1}} &<br>{{streetname2}}",
"<div style='color:{{basketcolor}}'>◇</div>",
"</p>"),
id = my_layer_id
)
DSNY Litter Basket Inventory Map
Standard Wire, High-end, Black Top, and Compacting