tidycensus::census_api_key(Sys.getenv(“CENSUS_API_KEY”)) google_api_key <- Sys.getenv(“GOOGLE_API_KEY”) stopifnot(nzchar(google_api_key))

#block groups bg <- suppressMessages( tidycensus::get_acs(geography = “block group”, # lab: BGs state = “NY”, county = c(“Broome”), variables = c(hhincome = ‘B19013_001’), year = 2023, survey = “acs5”, # filled geometry = TRUE, # filled (sf) output = “wide”) )

City boundary via tigris (lab did this for Decatur; my example will be Binghamton)

binghamton <- tigris::places(‘NY’) %>% dplyr::filter(NAME == ‘Binghamton’)

bg_city <- bg[binghamton,]

dplyr::glimpse(bg_city)

#next step = lat, long, radius from each block group getXYRadius <- function(polygon, gcs_id, pcs_id){

if (sf::st_crs(polygon) != sf::st_crs(pcs_id)){ polygon <- polygon %>% sf::st_transform(pcs_id) }

bb <- sf::st_bbox(polygon)

bb_corner <- sf::st_point(c(bb[1], bb[2])) %>% sf::st_sfc(crs = pcs_id)

#Get centroid bb_center <- bb %>% sf::st_as_sfc() %>% sf::st_centroid()

#Distance between center and corner (radius in meters in PCS) r <- sf::st_distance(bb_corner, bb_center)

#Convert centroid to GCS bb_center <- bb_center %>% sf::st_transform(gcs_id)

#lon/lat xy <- bb_center %>% sf::st_coordinates() %>% as.vector()

lon_lat_radius <- data.frame(x = xy[1], y = xy[2], r = r) return(lon_lat_radius) }

Pre-allocate results df (copied from lab example)

bg_city_xyr <- data.frame(x = numeric(nrow(bg_city)), y = numeric(nrow(bg_city)), r = numeric(nrow(bg_city)))

Fill with lon/lat/radius for each BG

for (i in 1:nrow(bg_city)){ bg_city_xyr[i,] <- bg_city[i, ] %>% getXYRadius(gcs_id = gcs_id, pcs_id = pcs_id) }

Visualize buffers vs. city (lab)

tmap::tmap_mode(‘view’) bg_city_xyr %>% sf::st_as_sf(coords = c(“x”,“y”), crs = sf::st_crs(bg_city)) %>% sf::st_buffer(dist = .$r) %>% tmap::tm_shape() + tmap::tm_polygons(alpha = 0.3, col = ‘red’) + tmap::tm_shape(bg_city) + tmap::tm_borders(col = ‘blue’)

1-mile circle example (lab had this with Atlanta; here Binghamton center for NY. Google searched coordinates btw)

lat <- 42.0987 lon <- -75.9180 radius <- 1609.34 # 1 mile in meters (filled) #from lab link below endpoint <- “https://places.googleapis.com/v1/places:searchNearby

body <- list( includedTypes = list(“park”), locationRestriction = list( circle = list( center = list(latitude = lat, longitude = lon), radius = radius ) ) )

resp <- httr::POST( endpoint, httr::add_headers( “Content-Type” = “application/json”, “X-Goog-Api-Key” = google_api_key, “X-Goog-FieldMask” = “places.displayName,places.formattedAddress,places.types” ), body = body, encode = “json” ) #check print(resp)

options(tigris_class = “sf”, tigris_use_cache = TRUE)

binghamton <- tigris::places(state = “NY”, year = 2023) |> dplyr::filter(NAME == “Binghamton”) |> sf::st_transform(4326)

bg <- tigris::block_groups(state = “NY”, county = “Broome”, year = 2023) |> sf::st_transform(4326)

bg_city <- bg[binghamton, ] |> sf::st_make_valid()

lab get xy radius

getXYRadius <- function(polygon, gcs_id, pcs_id){

if (sf::st_crs(polygon) != sf::st_crs(pcs_id)){ polygon <- polygon |> sf::st_transform(pcs_id) }

bb <- sf::st_bbox(polygon) bb_corner <- sf::st_point(c(bb[1], bb[2])) |> sf::st_sfc(crs = pcs_id) bb_center <- bb |> sf::st_as_sfc() |> sf::st_centroid()

r <- sf::st_distance(bb_corner, bb_center) # meters (PCS) bb_center <- bb_center |> sf::st_transform(gcs_id)

xy <- bb_center |> sf::st_coordinates() |> as.vector() data.frame(x = xy[1], y = xy[2], r = r) }

#Apply to every BG intersecting the city…. gcs_id <- 4326 pcs_id <- 32618
#UTM 18N (upstate NY)

bg_city_xyr <- data.frame(x = numeric(nrow(bg_city)), y = numeric(nrow(bg_city)), r = numeric(nrow(bg_city)))

for (i in 1:nrow(bg_city)){ bg_city_xyr[i, ] <- getXYRadius(bg_city[i, ], gcs_id = gcs_id, pcs_id = pcs_id) }

pts_wgs84 <- sf::st_as_sf(bg_city_xyr, coords = c(“x”,“y”), crs = gcs_id)

buffer in meters (PCS), then back to WGS84 for mapping

bufs_wgs84 <- pts_wgs84 |> sf::st_transform(pcs_id) |> sf::st_buffer(dist = as.numeric(bg_city_xyr$r)) |> sf::st_transform(gcs_id)

tmap::tmap_mode(“view”) tmap::tm_basemap(“Esri.WorldGrayCanvas”) + tmap::tm_shape(binghamton) + tmap::tm_borders(col = “dodgerblue”) + tmap::tm_shape(bufs_wgs84) + tmap::tm_borders(col = “orange”) + tmap::tm_shape(pts_wgs84) + tmap::tm_dots(col = “red”, size = 0.03)

nearbySearch <- function(lat, lon, radius, types_vec, fieldmask_vec, google_api_key){

endpoint <- “https://places.googleapis.com/v1/places:searchNearby

body <- list( includedTypes = as.list(types_vec), locationRestriction = list( circle = list( center = list(latitude = lat, longitude = lon), radius = radius ) ) # lab sometimes includes rankPreference; not required for the assignment )

resp <- httr::POST( endpoint, httr::add_headers( “Content-Type” = “application/json”, “X-Goog-Api-Key” = google_api_key, “X-Goog-FieldMask” = paste(fieldmask_vec, collapse = “,”) ), body = body, encode = “json” )

txt <- httr::content(resp, as = “text”, encoding = “UTF-8”) js <- jsonlite::fromJSON(txt, flatten = TRUE) if (!is.null(js\(places)) as.data.frame(js\)places) else data.frame() }

assignment-required 8 fields in the FieldMask according to week html

required_fields <- c( “places.id”, “places.displayName”, “places.formattedAddress”, “places.location”, “places.types”, “places.priceLevel”, “places.rating”, “places.userRatingCount” )

my two types for upstate NY

my_types <- c(“park”,“museum”)

#now POIs #testing lab test_list <- vector(“list”, 2) for(i in 1:2){ test_list[[i]] <- nearbySearch( lat = bg_city_xyr\(y[i], lon = bg_city_xyr\)x[i], radius = as.numeric(bg_city_xyr$r[i]), types_vec = my_types, fieldmask_vec = required_fields, google_api_key = google_api_key ) Sys.sleep(1)
}

data_list <- vector(“list”, nrow(bg_city_xyr)) for(i in seq_len(nrow(bg_city_xyr))){ data_list[[i]] <- nearbySearch( lat = bg_city_xyr\(y[i], lon = bg_city_xyr\)x[i], radius = as.numeric(bg_city_xyr$r[i]), types_vec = my_types, fieldmask_vec = required_fields, google_api_key = google_api_key ) Sys.sleep(1) } data_all <- dplyr::bind_rows(data_list)

saveRDS(data_all, here::here(“google_poi_binghamton.rds”))

#convert to sf with the map example in lab file data_all_sf <- data_all |> dplyr::rename( x = places.location.longitude, y = places.location.latitude ) |> dplyr::filter(!is.na(x) & !is.na(y)) |> sf::st_as_sf(coords = c(“x”,“y”), crs = 4326)

tmap::tmap_mode(“view”) tmap::tm_basemap(“Esri.WorldGrayCanvas”) + tmap::tm_shape(binghamton) + tmap::tm_borders(col = “steelblue”) + tmap::tm_shape(data_all_sf) + tmap::tm_dots( col = “places.rating”, size = “places.userRatingCount”, popup.vars = c( “Name” = “displayName.text”, “Address” = “formattedAddress”, “Rating” = “rating”, “Reviews” = “userRatingCount” ) )

#final questions for mini assignment answer #What city did you choose? ANSWER: Binghamton, NY :) #Which two place types? ANSWER: I chose parks and museums. cat(“How many rows?”, nrow(data_all), “”, sep = ““) #Briefly describe the clusters/gaps I see (spatial patterns) #ANSWER: I see a blue boundary that represents Binghamton, NY city with orange circles which represent the buffers from my block group (BG) centroids and finally I see black dots that represent centroids!!! This was a cool lab to do. Thanks!