## 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"))
<- st_read(here(data_path, "nyc-zip-shapes", "zips",
nyc_zips_geom "ZIP_CODE_040114.shp"), quiet = TRUE) %>%
select(ZIPCODE, PO_NAME, geometry)
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:
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:
<- "https://data.cityofnewyork.us/8znf-7b2c"
dsny_bin_url <- RSocrata::read.socrata(dsny_bin_url) %>%
bins # 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.
<- st_intersection(
zipbins st_make_valid(bins), st_make_valid(nyc_zips_geom)
%>%
) st_drop_geometry() %>%
st_as_sf(wkt = "point")
<- c("S" = "Standard Wire", "H" = "High-end",
bname "R" = "Black Top", "C" = "Compacting")
# DSNY Colors
<- function(x) {
bin_colorizer case_when( x == "S" ~ "#006749", x == "R" ~ "#1F2120",
== "H" ~ "#2B8CFD", x == "C" ~ "#FBB039",
x TRUE ~ "gray")
}
Here’s a look at the variable containing the data to be mapped, zipbins
:
Rows: 24,702
Columns: 14
$ basketid <int> 41260059, 41260057, 41260017, 4126…
$ baskettype <chr> "H", "R", "H", "R", "H", "H", "S",…
$ direction <chr> "SW", "SW", "NW", "SW", "NW", "SW"…
$ location_description <chr> "SW corner of SUTPHIN BLVD and FOC…
$ ownertype <chr> "D", "D", "D", "D", "D", "D", "D",…
$ section <chr> "QE126", "QE126", "QE126", "QE126"…
$ stateplane_snappedx <dbl> 1041895, 1041416, 1041466, 1040899…
$ stateplane_snappedy <dbl> 187226.9, 184748.9, 184818.8, 1848…
$ streetname1 <chr> "SUTPHIN BLVD", "ROCKAWAY BLVD", "…
$ streetname2 <chr> "FOCH BLVD", "145 ST", "145 ST", "…
$ objectid <int> 20681, 20679, 20401, 18497, 16143,…
$ ZIPCODE <chr> "11436", "11436", "11436", "11436"…
$ PO_NAME <chr> "Jamaica", "Jamaica", "Jamaica", "…
$ point <POINT> POINT (-73.79221 40.68026), 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.
<- "baskets" # for live-filtering
my_layer_id # in the next section
<- zipbins %>%
basketmap 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
`r sprintf("%s", " ")`<svg width='10' height='10'><rect width='10' height='10' style='fill:#006749;stroke-width:1'/></svg>
Standard Wire, High-end, Black Top, and Compacting