library(tidycensus)
## Warning: package 'tidycensus' was built under R version 4.4.3
library(sf)
## Warning: package 'sf' was built under R version 4.4.3
## Linking to GEOS 3.13.0, GDAL 3.10.1, PROJ 9.5.1; sf_use_s2() is TRUE
library(tmap)
## Breaking News: tmap 3.x is retiring. Please test v4, e.g. with
## remotes::install_github('r-tmap/tmap')
library(jsonlite)
## Warning: package 'jsonlite' was built under R version 4.4.3
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()  masks stats::filter()
## ✖ purrr::flatten() masks jsonlite::flatten()
## ✖ dplyr::lag()     masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(httr)
library(jsonlite)
library(reshape2)
## 
## Attaching package: 'reshape2'
## 
## The following object is masked from 'package:tidyr':
## 
##     smiths
library(here)
## here() starts at C:/Users/HojungYu/OneDrive/GT_Semester3/Urban_Analytics
library(knitr)
library(dplyr)

Collecting POI data using Google Places API

1. Choose two place types for the includedTypes parameter in the Google Places Nearby Search API. Refer to the API documentation for options.

I chose ‘coffe_shop’ and ‘pub’ for my assignment.

2. Select a U.S. city. Use the tigris package to obtain an sf object of the city boundary.

Buford is my area of interest.

bg <- suppressMessages(
  tidycensus::get_acs(geography = "block group", # or "block", "tract", "county", "state" etc.
                      state = "GA",
                      county = c("Barrow","Gwinnett"),
                      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
)
##   |                                                                              |                                                                      |   0%  |                                                                              |=                                                                     |   1%  |                                                                              |=                                                                     |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |===                                                                   |   4%  |                                                                              |===                                                                   |   5%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |======                                                                |   8%  |                                                                              |=======                                                               |   9%  |                                                                              |=======                                                               |  10%  |                                                                              |========                                                              |  11%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |==========                                                            |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  17%  |                                                                              |============                                                          |  18%  |                                                                              |=============                                                         |  18%  |                                                                              |==============                                                        |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |===============                                                       |  21%  |                                                                              |================                                                      |  22%  |                                                                              |=================                                                     |  24%  |                                                                              |==================                                                    |  26%  |                                                                              |===================                                                   |  27%  |                                                                              |====================                                                  |  29%  |                                                                              |=====================                                                 |  30%  |                                                                              |======================                                                |  32%  |                                                                              |=======================                                               |  33%  |                                                                              |========================                                              |  34%  |                                                                              |=========================                                             |  35%  |                                                                              |=========================                                             |  36%  |                                                                              |==========================                                            |  37%  |                                                                              |==========================                                            |  38%  |                                                                              |===========================                                           |  39%  |                                                                              |============================                                          |  40%  |                                                                              |=============================                                         |  41%  |                                                                              |==============================                                        |  42%  |                                                                              |===============================                                       |  45%  |                                                                              |================================                                      |  46%  |                                                                              |=================================                                     |  47%  |                                                                              |=================================                                     |  48%  |                                                                              |==================================                                    |  49%  |                                                                              |===================================                                   |  50%  |                                                                              |====================================                                  |  51%  |                                                                              |=====================================                                 |  53%  |                                                                              |=======================================                               |  56%  |                                                                              |========================================                              |  57%  |                                                                              |==========================================                            |  60%  |                                                                              |==========================================                            |  61%  |                                                                              |===========================================                           |  62%  |                                                                              |============================================                          |  63%  |                                                                              |=============================================                         |  65%  |                                                                              |==============================================                        |  66%  |                                                                              |===============================================                       |  68%  |                                                                              |=================================================                     |  70%  |                                                                              |==================================================                    |  72%  |                                                                              |====================================================                  |  74%  |                                                                              |=====================================================                 |  75%  |                                                                              |=====================================================                 |  76%  |                                                                              |======================================================                |  78%  |                                                                              |=======================================================               |  79%  |                                                                              |========================================================              |  80%  |                                                                              |=========================================================             |  81%  |                                                                              |==========================================================            |  83%  |                                                                              |============================================================          |  86%  |                                                                              |=============================================================         |  86%  |                                                                              |==============================================================        |  88%  |                                                                              |==============================================================        |  89%  |                                                                              |===============================================================       |  90%  |                                                                              |===============================================================       |  91%  |                                                                              |================================================================      |  92%  |                                                                              |==================================================================    |  94%  |                                                                              |===================================================================   |  95%  |                                                                              |===================================================================   |  96%  |                                                                              |====================================================================  |  97%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================|  99%  |                                                                              |======================================================================| 100%
# City boundary
buford <- tigris::places('GA') %>% filter(NAME == 'Buford')
## Retrieving data for the year 2022
##   |                                                                              |                                                                      |   0%  |                                                                              |=                                                                     |   1%  |                                                                              |=                                                                     |   2%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |===                                                                   |   4%  |                                                                              |====                                                                  |   5%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |======                                                                |   9%  |                                                                              |=======                                                               |  10%  |                                                                              |========                                                              |  11%  |                                                                              |=========                                                             |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |==========                                                            |  14%  |                                                                              |===========                                                           |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  17%  |                                                                              |============                                                          |  18%  |                                                                              |=============                                                         |  18%  |                                                                              |==============                                                        |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |===============                                                       |  21%  |                                                                              |===============                                                       |  22%  |                                                                              |================                                                      |  23%  |                                                                              |=================                                                     |  24%  |                                                                              |=================                                                     |  25%  |                                                                              |==================                                                    |  26%  |                                                                              |===================                                                   |  27%  |                                                                              |===================                                                   |  28%  |                                                                              |====================                                                  |  29%  |                                                                              |=====================                                                 |  29%  |                                                                              |=====================                                                 |  30%  |                                                                              |======================                                                |  31%  |                                                                              |=======================                                               |  32%  |                                                                              |=======================                                               |  33%  |                                                                              |========================                                              |  35%  |                                                                              |=========================                                             |  36%  |                                                                              |==========================                                            |  36%  |                                                                              |==========================                                            |  37%  |                                                                              |==========================                                            |  38%  |                                                                              |===========================                                           |  38%  |                                                                              |============================                                          |  39%  |                                                                              |============================                                          |  40%  |                                                                              |=============================                                         |  41%  |                                                                              |=============================                                         |  42%  |                                                                              |==============================                                        |  42%  |                                                                              |==============================                                        |  43%  |                                                                              |===============================                                       |  44%  |                                                                              |================================                                      |  45%  |                                                                              |================================                                      |  46%  |                                                                              |=================================                                     |  47%  |                                                                              |=================================                                     |  48%  |                                                                              |==================================                                    |  48%  |                                                                              |==================================                                    |  49%  |                                                                              |===================================                                   |  50%  |                                                                              |===================================                                   |  51%  |                                                                              |====================================                                  |  51%  |                                                                              |=====================================                                 |  52%  |                                                                              |=====================================                                 |  53%  |                                                                              |======================================                                |  54%  |                                                                              |======================================                                |  55%  |                                                                              |=======================================                               |  56%  |                                                                              |========================================                              |  57%  |                                                                              |========================================                              |  58%  |                                                                              |=========================================                             |  58%  |                                                                              |==========================================                            |  60%  |                                                                              |===========================================                           |  61%  |                                                                              |============================================                          |  62%  |                                                                              |============================================                          |  63%  |                                                                              |============================================                          |  64%  |                                                                              |=============================================                         |  65%  |                                                                              |==============================================                        |  65%  |                                                                              |==============================================                        |  66%  |                                                                              |===============================================                       |  67%  |                                                                              |===============================================                       |  68%  |                                                                              |================================================                      |  68%  |                                                                              |================================================                      |  69%  |                                                                              |=================================================                     |  70%  |                                                                              |=================================================                     |  71%  |                                                                              |==================================================                    |  71%  |                                                                              |==================================================                    |  72%  |                                                                              |===================================================                   |  72%  |                                                                              |===================================================                   |  73%  |                                                                              |=====================================================                 |  76%  |                                                                              |======================================================                |  77%  |                                                                              |=======================================================               |  78%  |                                                                              |=======================================================               |  79%  |                                                                              |=========================================================             |  82%  |                                                                              |==========================================================            |  83%  |                                                                              |==========================================================            |  84%  |                                                                              |===========================================================           |  84%  |                                                                              |===========================================================           |  85%  |                                                                              |============================================================          |  86%  |                                                                              |=============================================================         |  87%  |                                                                              |=============================================================         |  88%  |                                                                              |==============================================================        |  88%  |                                                                              |==============================================================        |  89%  |                                                                              |===============================================================       |  89%  |                                                                              |===============================================================       |  90%  |                                                                              |================================================================      |  92%  |                                                                              |=================================================================     |  92%  |                                                                              |=================================================================     |  93%  |                                                                              |==================================================================    |  94%  |                                                                              |===================================================================   |  95%  |                                                                              |====================================================================  |  97%  |                                                                              |====================================================================  |  98%  |                                                                              |===================================================================== |  98%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================| 100%
library("mapview")
## Warning: package 'mapview' was built under R version 4.4.3
# Get BGs intersecting with the City of Decatur boundary
bg_buford <- bg[buford,]

mapview(bg_buford)

3.Divide the city into small areas using one of the following methods and prepare locationRestriction parameter values (longitude, latitude, and radius) for each area:

  • Method 1: Census Block Groups (BGs) that intersect the city polygon
  • Method 2: Fishnet grid cells based on the city polygon

I selected method 2 for dividing my area of interest.

# Define EPSG codes for GCS (WGS 84) and PCS (WGS 84 / UTM zone 16N)
# Since my city is Savannah, 
gcs_id <- 4326
pcs_id <- 32616 # WGS 84 / UTM zone 16N

# Get bbox for Buford
buford_bb <- buford %>% st_transform(pcs_id) %>% st_bbox()

# Find coordinates for the four sides of the bbox
west <- buford_bb[1]
east <- buford_bb[3]
south <- buford_bb[2]
north <- buford_bb[4]

# Split the bbox into a grid
fishnet_n <- 8
dist <- abs(east - west)/fishnet_n

# Fishnet points
fish_x <- seq(from = west, to = east, by = dist)
fish_y <- seq(from = south, to = north, by = dist)

fishnet <- expand.grid(fish_x, fish_y) %>%
  rename(x = Var1, y = Var2) %>%
  st_as_sf(coords = c('x', 'y'), crs = pcs_id)

fishnet$x <- fishnet %>% st_transform(gcs_id) %>% st_coordinates() %>% .[,1]
fishnet$y <- fishnet %>% st_transform(gcs_id) %>% st_coordinates() %>% .[,2]
fishnet$r <- dist*(2^(1/2)/2)

4. Verify coverage to ensure there are no uncovered areas by visualizing your locationRestriction parameters on a map:

  1. Create a POINT sf object using longitude, latitude, and sf::st_as_sf().
  2. Create buffers using radius and sf::st_buffer().
  3. Build an interactive map displaying (1) the city boundary and (2) the buffer objects with the tmap package.
# step 1 
pts_ll <- fishnet |>
  st_drop_geometry() |>
  st_as_sf(coords = c("x", "y"), crs = gcs_id)  

# step 2
pts_utm <- pts_ll |> st_transform(pcs_id)
bufs    <- st_buffer(pts_utm, dist = fishnet$r)
 
# step 3
tmap_mode("view")
## tmap mode set to interactive viewing
tm_basemap("CartoDB.Positron") +
  tm_shape(st_transform(buford, gcs_id)) +
  tm_borders(lwd = 2, col = "black") +
  tm_shape(st_transform(bufs, gcs_id)) +
  tm_polygons(alpha = 0.2, col = "red", border.col = NA) +
  tm_shape(pts_ll) +
  tm_dots(size = 0.02, col = "blue") +

mapview(bufs)

5. Collect POI data using the Nearby Search in the Google Places API.

  • Include two place types in the includedTypes parameter.
  • For FieldMask, include these 8 fields:
  • places.id; places.displayName; places.formattedAddress; places.location; places.types; places.priceLevel; places.rating; places.userRatingCount
  • You may also add other fields of interest, but these 8 must be included.
  • Parse the API response into a data frame.
  • Export the POI dataset into an RData or RDS file.
library(httr)

google_api_key <- Sys.getenv("API_KEY")

# function
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
      )
    ),
    rankPreference = "DISTANCE" 
  )
  
  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"
  )
  
  #Parse the API response into a data frame.
  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)
}

bg_buford_xy <- fishnet

# allocate the list first
n <- nrow(bg_buford_xy)
coffee_list <- vector("list", n)
bar_list    <- vector("list", n)

for (i in seq_len(nrow(bg_buford_xy))) {
  # COFFEE
  coffee_list[[i]] <- nearbySearch(
    lat = bg_buford_xy$y[i],
    lon = bg_buford_xy$x[i],
    radius = bg_buford_xy$r[i],
    types_vec = 'coffee_shop', # coffee shop and pub
    fieldmask_vec = c("places.id",
                      "places.displayName",
                      "places.formattedAddress",
                      "places.location",
                      "places.types",
                      "places.priceLevel",
                      "places.rating",
                      "places.userRatingCount"),
    google_api_key = google_api_key
  )

  Sys.sleep(1) # throttle requests to respect QPS quotas and avoid 429 rate-limit errors

  # PUB
  bar_list[[i]] <- nearbySearch(
    lat = bg_buford_xy$y[i],
    lon = bg_buford_xy$x[i],
    radius = bg_buford_xy$r[i],
    types_vec = 'bar', # coffee shop and pub
    fieldmask_vec = c("places.id",
                      "places.displayName",
                      "places.formattedAddress",
                      "places.location",
                      "places.types",
                      "places.priceLevel",
                      "places.rating",
                      "places.userRatingCount"),
    google_api_key = google_api_key
  )
  
  Sys.sleep(1)
  
}

# Combine all data frames
coffee_all <- dplyr::bind_rows(coffee_list) |>
  mutate(source_type = "coffee_shop") |>
  distinct(places.id, .keep_all = TRUE) # dedupe across overlapping buffers

bar_all <- dplyr::bind_rows(bar_list) |>
  mutate(source_type = "bar") |>
  distinct(places.id, .keep_all = TRUE)

# Export the POI into an RDS file
saveRDS(coffee_all, here::here("buford_poi_coffee_shops.rds"))
saveRDS(bar_all,    here::here("buford_poi_bars.rds"))

6. Map the collected POIs

  • Convert the POI dataset into an sf object using sf::st_as_sf().
  • Create an interactive map showing both the POIs and the city boundary.
# Convert the data to an sf object using XY coordinates
coffee_all_sf <- coffee_all %>%
  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)

bar_all_sf <- bar_all %>%
  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)

coffee_all_sf$type <- "Coffee"
bar_all_sf$type    <- "Bar"

poi_sf <- rbind(coffee_all_sf, bar_all_sf)

# Map
tm_shape(poi_sf) + 
  tm_dots(col = "type", 
          size = "places.userRatingCount",
          palette = c("Coffee" = "brown", "Bar" = "darkblue"),
          border.lwd=0.5,
          popup.vars = c("Name" = "places.displayName.text",
                         "Address" = "places.formattedAddress",
                         "Rating" = "places.rating",
                         "Rating Count" = "places.userRatingCount",
                         "Price Level" = "places.priceLevel")) +
  tm_shape(buford) + 
  tm_borders()
## Legend for symbol sizes not available in view mode.

7. Answer the following questions at the end of your script:

  • What city did you choose?

I chose Buford city in Gwinnett County. It is famous for the largest mall in the state of Georgia, Mall of Georgia. It is also known for its small-town charm and local restaurants.

  • Which two place types did you select?

I selected ‘coffee_shop’ and ‘bar’ to see the difference between nightlife and day-life.

  • How many rows does your dataset contain?

60 rows are in my ‘poi_sf’.

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

Around the Mall of Georgia, I noticed a concentration of POIs with high numbers of reviews. The area has more bars than coffee shops. It reflects place’s role as a shopping destination where visitors are more likely to engage in dining and nightlife activities than in coffee-centered activities such as studying or working.

In contrast, near Sugar hills and golden Pkwy, coffee shops outnumber bars. This is because the area functions more as local community hub, where daytime activities and neighborhood interactions are more prominent.

Overall, coffee shops are usually clustered near community center while bars are spread out along major roads. These spatial patterns highlight the differing rhythms of suburban life, which can be further investigated through analyzing the common behavior pattern of day-life and nightlife of suburban life.