library(sf)
library(dplyr)
library(readr)
library(stringr)
library(ggplot2)
library(tigris)
library(scales)
This project inventories active business licenses and active PLCB liquor licenses within the East Passyunk study area to support a corridor-scale understanding of commercial activity and neighborhood character.
The deliverable is a dashboard-style summary: a map of active licenses and a compact breakdown of license composition.
bl <- st_read("data/business_licenses.geojson", quiet = TRUE) %>%
st_transform(2272) #Philly/SEPA analysis ESPG
ep_boundary <- st_read("data/study_areas_2025_1.shp", quiet = TRUE) %>%
st_transform(2272)
plcb_raw <- readr::read_csv("data/PHL_PLCB_geocoded.csv", show_col_types = FALSE) %>%
mutate(
lon = as.numeric(lon),
lat = as.numeric(lat)
)
plcb_sf <- plcb_raw %>%
mutate(
lon = as.numeric(lon),
lat = as.numeric(lat)
) %>%
filter(!is.na(lon), !is.na(lat)) %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326, remove = FALSE) %>%
st_transform(2272)
bl_ep <- st_intersection(bl,ep_boundary)
plcb_ep <- st_intersection(plcb_sf, ep_boundary)
bl_ep_active <- bl_ep %>%
filter(licensestatus == "Active") %>% # filter to active status
mutate(
category = case_when(
licensetype %in% c(
"Food Preparing and Serving",
"Food Preparing and Serving (30+ SEATS)",
"Sidewalk Cafe",
"Food Establishment, Retail Permanent Location",
"Food Caterer",
"Food Estab, Retail Non-Permanent Location (Annual)",
"Food Manufacturer / Wholesaler"
) ~ "Dining",
licensetype %in% c(
"Amusement",
"Limited Lodging Operator",
"Special Assembly Occupancy"
) ~ "Entertainment",
TRUE ~ "Other"
),
source = "Business License"
) %>%
arrange(addressobjectid, category) %>%
group_by(addressobjectid) %>%
slice(1) %>%
ungroup() %>%
st_zm(drop = TRUE, what = "ZM")
Filter for PLCB active licenses
plcb_ep_active <- plcb_ep %>%
filter(Status == "Active") %>%
mutate(
category = "PLCB License",
source = "PLCB License",
licensetype = License.Type) %>%
st_zm(drop = TRUE, what = "ZM")
licenses_ep <- bind_rows(
bl_ep_active %>%
transmute(
source,
category,
licensetype = licensetype,
name = business_name,
address = address,
geometry
),
plcb_ep_active %>%
transmute(
source,
category,
licensetype = License.Type,
name = Licensee,
address = Premises.Address,
geometry
)
)
# quick sanity check
nrow(licenses_ep)
## [1] 255
## 4.1 Key Counts by Category
summary_counts <- licenses_ep %>%
st_drop_geometry() %>%
count(category, sort = TRUE) %>%
mutate(
percent = round(100 * n / sum(n), 2)
)
summary_counts
## 4.2 License Composition Chart
license_type_summary <- licenses_ep %>%
st_drop_geometry() %>%
count(category, licensetype, sort = TRUE) %>%
group_by(category) %>%
mutate(
pct_within_category = n / sum(n)
) %>%
ungroup()
ggplot(
license_type_summary,
aes(x = reorder(licensetype, n), y = n, fill = category)
) +
geom_col(alpha = 0.9) +
coord_flip() +
geom_text(aes(label = n), hjust = -0.15, size = 4) +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(
title = "Food, Beverage & Entertainment License Distribution — East Passyunk",
subtitle = "Active licenses only",
x = NULL,
y = "Count",
fill = "Category"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom"
)
## 5.1 Local Roads for Context
options(tigris_use_cache = TRUE)
roads <- roads(state = "PA", county = "Philadelphia") %>%
st_transform(2272)
ep_buffer <- st_buffer(ep_boundary, dist = 800) # ~800 ft buffer
roads_clipped <- st_intersection(roads, ep_buffer)
## 5.2 Map of Active Licenses in East Passyunk
category_palette <- c(
"Dining" = "#7b3294",
"Entertainment" = "#686569",
"Other" = "#ADB068",
"PLCB License" = "#D17621"
)
ggplot() +
geom_sf(data = roads_clipped, color = "gray55", linewidth = 0.25) +
geom_sf(
data = ep_boundary,
fill = NA,
color = "#F50A0A",
linewidth = 0.9,
linetype = "dashed"
) +
geom_sf(
data = licenses_ep,
aes(color = category),
size = 2.2,
alpha = 0.9
) +
scale_color_manual(values = category_palette) +
labs(
title = "Active Business and Liquor Licenses in East Passyunk",
color = "License Category"
) +
theme_void() +
theme(
plot.background = element_rect(fill = "black", color = NA),
panel.background = element_rect(fill = "black", color = NA),
plot.title = element_text(
hjust = 0.5,
color = "white",
size = 16,
face = "bold"
),
legend.background = element_rect(fill = "black", color = NA),
legend.key = element_rect(fill = "black", color = NA),
legend.text = element_text(color = "white"),
legend.title = element_text(color = "white", face = "bold")
)