Mini Assignment 1

Import Libraries

library(tidycensus)
library(sf)
library(tmap)
library(jsonlite)
library(tidyverse)
library(httr)
library(jsonlite)
library(reshape2)
library(here)
library(knitr)
library(httr)

1.Decide Two Place types

includedTypes = list("korean_restaurant", "japanese_restaurant")

2.Select a U.S. city & Download Boundary

Method1. Prepare Census Block Groups that intersect the city polygon (Ithaca)

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,]

Method1. Get Radius, Latitude, Longitude

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)
}

4. Verify coverage

(1) City of Ithaca boundary

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)

(2) The buffer objects

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)

5. Collect POI data

Setup Nearby Search Function

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)
}

Download 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"))

Interactive map

# 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()

Answer for questions

1. What city did you choose?

Ithaca, Newyork

2. Which two place types did you select?

korean_restaurant, japanese_restaurant

3. How many rows does your dataset contain?

18 rows (after drop duplicated POIs)

4. Upon visual inspection, do you notice any spatial patterns in how POIs are distributed across the city?

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.

5. Did you observe any other interesting findings?

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.