Import Libraries
library(tidycensus)
library(sf)
library(tmap)
library(jsonlite)
library(tidyverse)
library(httr)
library(jsonlite)
library(reshape2)
library(here)
library(knitr)
library(httr)
includedTypes = list("korean_restaurant", "japanese_restaurant")
tidycensus::census_api_key(Sys.getenv("CENSUS_API_KEY"))
# Get block groups
bg <- suppressMessages(
tidycensus::get_acs(geography = "block group", # or "block", "tract", "county", "state" etc.
state = "NY",
county = c("Tompkins"),
variables = c(hhincome = 'B19013_001'),
year = 2023,
survey = "acs5", # we typically use American Community Survey 5-year estimates
geometry = TRUE, # we need an sf object
output = "wide") # wide vs. long
)
# City of Ithaca boundary
ithaca <- tigris::places('NY') %>% filter(NAME == 'Ithaca')
# Get Block groups
bg_ithaca <- bg[ithaca,]
getXYRadius <- function(polygon, gcs_id, pcs_id){
if (st_crs(polygon) != st_crs(pcs_id)){
polygon <- polygon %>% st_transform(pcs_id)
}
bb <- st_bbox(polygon)
bb_corner <- st_point(c(bb[1], bb[2])) %>% st_sfc(crs = pcs_id)
bb_center <- bb %>% st_as_sfc() %>% st_centroid()
r <- st_distance(bb_corner, bb_center)
bb_center <- bb_center %>% st_transform(gcs_id)
xy <- bb_center %>% st_coordinates() %>% as.vector()
lon_lat_radius <- data.frame(x = xy[1],
y = xy[2],
r = r)
return(lon_lat_radius)
}
gcs_id <- 4326
pcs_id <- 32618 #UTM Zone 18N
bg_ithaca_xyr <- data.frame(x = numeric(nrow(bg_ithaca)),
y = NA,
r = NA)
for (i in 1:nrow(bg_ithaca)){
bg_ithaca_xyr[i,] <- bg_ithaca[i, ] %>%
getXYRadius(gcs_id = gcs_id,
pcs_id = pcs_id)
}
bg_ithaca <- bg_ithaca %>%
select(GEOID,
hhincome = hhincomeE)
tmap_mode("view")
tm_shape(bg_ithaca) + tm_borders(lwd = 2) +
tm_shape(ithaca) + tm_polygons(col = 'red', alpha = 0.4)
bg_ithaca_xyr %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(bg_ithaca)) %>%
st_buffer(dist = .$r) %>%
tm_shape(.) + tm_polygons(alpha = 0.2, col = 'red') +
tm_shape(bg_ithaca) + tm_borders(col= 'blue', lwd = 0.8) +
tm_shape(ithaca) + tm_borders(lwd = 2.4)
nearbySearch <- function(lat, lon, radius, types_vec, fieldmask_vec, google_api_key){
endpoint <- "https://places.googleapis.com/v1/places:searchNearby" # Nearby Search endpoint
body <- list(
includedTypes = as.list(types_vec),
locationRestriction = list(
circle = list(
center = list(latitude = lat, longitude = lon),
radius = radius
)
),
rankPreference = "DISTANCE" # WHAT does this parameter do? and WHY is this added?
)
resp <- POST(
endpoint,
add_headers(
"Content-Type" = "application/json",
"X-Goog-Api-Key" = google_api_key,
"X-Goog-FieldMask" = paste(fieldmask_vec, collapse = ",")),
body = body,
encode = "json"
)
data <- content(resp, as="text") %>%
jsonlite::fromJSON(flatten = T) %>%
as.data.frame()
if (nrow(data) == 20){
print("WARNING: The response has 20 rows! Consider using a smaller spatial unit.")
}
return(data)
}
data_list <- vector("list", nrow(bg_ithaca_xyr))
for (i in seq_len(nrow(bg_ithaca_xyr))) {
data_list[[i]] <- nearbySearch(
lat = bg_ithaca_xyr$y[i],
lon = bg_ithaca_xyr$x[i],
radius = bg_ithaca_xyr$r[i],
types_vec = includedTypes, # korean_restaurant, japanese_restaurant
fieldmask_vec = c("places.id",
"places.displayName",
"places.formattedAddress",
"places.location",
"places.types",
"places.priceLevel",
"places.rating",
"places.userRatingCount",
"places.reviews",
"places.reviewSummary"),
google_api_key = Sys.getenv("GOOGLE_API_KEY")
)
Sys.sleep(1)
}
# Combine all data frames
data_all <- dplyr::bind_rows(data_list)
data_all_unique <- data_all %>%
distinct(places.id, .keep_all = TRUE)
# Saving the data
saveRDS(data_all_unique, here("ithaca_poi_data.rds"))
# Convert the data to an sf object using XY coordinates
data_all_sf <- data_all_unique %>%
rename(x = places.location.longitude, y = places.location.latitude) %>%
filter(!is.na(x) & !is.na(y)) %>%
st_as_sf(coords = c("x", "y"), crs = 4326)
# Map
tm_shape(data_all_sf) +
tm_dots(col = "places.rating",
size = "places.userRatingCount",
palette = "magma",
popup.vars = c("Name" = "places.displayName.text",
"Rating" = "places.rating",
"Rating Count" = "places.userRatingCount")) +
tm_shape(ithaca) +
tm_borders()
Ithaca, Newyork
korean_restaurant, japanese_restaurant
18 rows (after drop duplicated POIs)
Although there are only a few Korean and Japanese restaurants in Ithaca, NY, one cluster is clearly visible near the entrance of Cornell University on the east side. Another group of restaurants is concentrated in the downtown area at the center of the city.
When I lived in Ithace, I found Maru Ramen to be a really good spot. On the map, it appears slightly west of central Ithaca, and its rating is higher than the asian (Korean, and Japanese) restaurants located in College Town on the east side.