Major Assignment 2 - Template

Originally written by Bon Woo Koo & Subhro Guhathakurta; modified by Uijeong Hwang

2024-10-30

Section 1. Choose your Census Tracts.

Section 2. OSM, GSV, and computer vision.

library(tidyverse)
library(tidycensus)
library(osmdata)
library(sfnetworks)
library(units)
library(sf)
library(tidygraph)
library(tmap)
library(here)
# TASK ////////////////////////////////////////////////////////////////////////
# 1. Set up your api key here
census_api_key(Sys.getenv("CENSUS_API_KEY"))
## To install your API key for use in future sessions, run this function with `install = TRUE`.
# //TASK //////////////////////////////////////////////////////////////////////



# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Download Census Tract polygon for Fulton and DeKalb
tract <- get_acs("tract", 
                 variables = c('tot_pop' = 'B01001_001'),
                 year = 2022,
                 state = "GA", 
                 county = c("Fulton", "DeKalb"), 
                 geometry = TRUE)
## Getting data from the 2018-2022 5-year ACS
## Downloading feature geometry from the Census website.  To cache shapefiles for use in future sessions, set `options(tigris_use_cache = TRUE)`.
##   |                                                                              |                                                                      |   0%  |                                                                              |                                                                      |   1%  |                                                                              |=                                                                     |   1%  |                                                                              |=                                                                     |   2%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |==                                                                    |   4%  |                                                                              |===                                                                   |   4%  |                                                                              |====                                                                  |   5%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |======                                                                |   8%  |                                                                              |======                                                                |   9%  |                                                                              |=======                                                               |   9%  |                                                                              |=======                                                               |  10%  |                                                                              |========                                                              |  11%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |==========                                                            |  14%  |                                                                              |==========                                                            |  15%  |                                                                              |===========                                                           |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  17%  |                                                                              |============                                                          |  18%  |                                                                              |=============                                                         |  18%  |                                                                              |=============                                                         |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |==============                                                        |  21%  |                                                                              |===============                                                       |  21%  |                                                                              |===============                                                       |  22%  |                                                                              |================                                                      |  23%  |                                                                              |================                                                      |  24%  |                                                                              |=================                                                     |  24%  |                                                                              |==================                                                    |  25%  |                                                                              |==================                                                    |  26%  |                                                                              |===================                                                   |  27%  |                                                                              |====================                                                  |  28%  |                                                                              |====================                                                  |  29%  |                                                                              |=====================                                                 |  29%  |                                                                              |=====================                                                 |  30%  |                                                                              |======================                                                |  31%  |                                                                              |======================                                                |  32%  |                                                                              |=======================                                               |  32%  |                                                                              |=======================                                               |  33%  |                                                                              |========================                                              |  34%  |                                                                              |========================                                              |  35%  |                                                                              |=========================                                             |  36%  |                                                                              |==========================                                            |  37%  |                                                                              |==========================                                            |  38%  |                                                                              |===========================                                           |  38%  |                                                                              |===========================                                           |  39%  |                                                                              |============================                                          |  40%  |                                                                              |=============================                                         |  41%  |                                                                              |=============================                                         |  42%  |                                                                              |==============================                                        |  43%  |                                                                              |===============================                                       |  45%  |                                                                              |================================                                      |  46%  |                                                                              |=================================                                     |  48%  |                                                                              |==================================                                    |  49%  |                                                                              |===================================                                   |  50%  |                                                                              |====================================                                  |  51%  |                                                                              |====================================                                  |  52%  |                                                                              |=====================================                                 |  52%  |                                                                              |=====================================                                 |  53%  |                                                                              |======================================                                |  54%  |                                                                              |=======================================                               |  55%  |                                                                              |=======================================                               |  56%  |                                                                              |========================================                              |  57%  |                                                                              |=========================================                             |  58%  |                                                                              |=========================================                             |  59%  |                                                                              |==========================================                            |  60%  |                                                                              |===========================================                           |  61%  |                                                                              |============================================                          |  63%  |                                                                              |=============================================                         |  64%  |                                                                              |==============================================                        |  66%  |                                                                              |===============================================                       |  67%  |                                                                              |===============================================                       |  68%  |                                                                              |================================================                      |  69%  |                                                                              |=================================================                     |  70%  |                                                                              |==================================================                    |  71%  |                                                                              |===================================================                   |  72%  |                                                                              |===================================================                   |  73%  |                                                                              |====================================================                  |  74%  |                                                                              |=====================================================                 |  75%  |                                                                              |=====================================================                 |  76%  |                                                                              |======================================================                |  77%  |                                                                              |=======================================================               |  78%  |                                                                              |=======================================================               |  79%  |                                                                              |========================================================              |  80%  |                                                                              |=========================================================             |  81%  |                                                                              |=========================================================             |  82%  |                                                                              |==========================================================            |  83%  |                                                                              |===========================================================           |  84%  |                                                                              |===========================================================           |  85%  |                                                                              |============================================================          |  85%  |                                                                              |=============================================================         |  87%  |                                                                              |=============================================================         |  88%  |                                                                              |==============================================================        |  89%  |                                                                              |===============================================================       |  90%  |                                                                              |===============================================================       |  91%  |                                                                              |================================================================      |  92%  |                                                                              |=================================================================     |  93%  |                                                                              |=================================================================     |  94%  |                                                                              |===================================================================   |  95%  |                                                                              |===================================================================   |  96%  |                                                                              |====================================================================  |  97%  |                                                                              |====================================================================  |  98%  |                                                                              |===================================================================== |  98%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================| 100%
# =========== NO MODIFY ZONE ENDS HERE ========================================



# TASK ////////////////////////////////////////////////////////////////////////
# The purpose of this TASK is to create one bounding box for walkable Census Tract and another bounding box for unwalkable Census Tract.
# As long as you generate what's needed for the subsequent codes, you are good. The numbered list of tasks below is to provide some hints.
# 1. Write the GEOID of walkable & unwalkable Census Tracts. e.g., tr1_ID <- c("13121001205", "13121001206")
# 2. Extract the selected Census Tracts using tr1_ID & tr2_ID
# 3. Create their bounding boxes using st_bbox(), and 
# 4. assign them to tract_1_bb and tract_1_bb, respectively.

# For the walkable Census Tract(s)
# 1. 
tr1_ID <- c("13121001101", "13121001102")
# 2~4
tract_1_bb <- tract %>% 
  filter(GEOID %in% tr1_ID) %>%
  st_bbox()

# For the unwalkable Census Tract(s)  
# 1.
tr2_ID <- c("13089023214", "13089023442")

# 2~4
tract_2_bb <- tract %>% 
  filter(GEOID %in% tr2_ID) %>%
  st_bbox()
# //TASK //////////////////////////////////////////////////////////////////////

  
  
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Get OSM data for the two bounding box
osm_1 <- opq(bbox = tract_1_bb) %>%
  add_osm_feature(key = 'highway', 
                  value = c("motorway", "trunk", "primary", 
                            "secondary", "tertiary", "unclassified",
                            "residential")) %>%
  osmdata_sf() %>% 
  osm_poly2line()

osm_2 <- opq(bbox = tract_2_bb) %>%
  add_osm_feature(key = 'highway', 
                  value = c("motorway", "trunk", "primary", 
                            "secondary", "tertiary", "unclassified",
                            "residential")) %>%
  osmdata_sf() %>% 
  osm_poly2line()
# =========== NO MODIFY ZONE ENDS HERE ========================================



# TASK ////////////////////////////////////////////////////////////////////////
# 1. Convert osm_1 and osm_2 to sfnetworks objects (set directed = FALSE)
# 2. Clean the network by (1) deleting parallel lines and loops, (2) create missing nodes, and (3) remove pseudo nodes, 
# 3. Add a new column named length using edge_length() function.
net1 <- osm_1$osm_lines %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  sfnetworks::as_sfnetwork(directed = FALSE) %>%
  activate("edges") %>%
  filter(!edge_is_multiple()) %>%
  filter(!edge_is_loop()) %>%
  convert(., sfnetworks::to_spatial_subdivision) %>%
  convert(., sfnetworks::to_spatial_smooth) %>%
  mutate(length = edge_length())
## Warning: to_spatial_subdivision assumes attributes are constant over geometries
net2 <- osm_2$osm_lines %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  as_sfnetwork(directed = FALSE) %>%
  activate("edges") %>%
  filter(!edge_is_multiple()) %>%
  filter(!edge_is_loop()) %>%
  convert(., sfnetworks::to_spatial_subdivision) %>%
  convert(., sfnetworks::to_spatial_smooth) %>%
  mutate(length = edge_length())
## Warning: to_spatial_subdivision assumes attributes are constant over geometries
# //TASK //////////////////////////////////////////////////////////////////////
  
  
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# OSM for the walkable part
edges_1 <- net1 %>% 
  # Extract 'edges'
  st_as_sf("edges") %>% 
  # Drop segments that are too short (100m)
  mutate(length = as.vector(length)) %>% 
  filter(length > 100) %>% 
  # Add a unique ID for each edge
  mutate(edge_id = seq(1,nrow(.)),
         is_walkable = "walkable")

# OSM for the unwalkable part
edges_2 <- net2 %>% 
  # Extract 'edges'
  st_as_sf("edges") %>%
  # Drop segments that are too short (100m)
  mutate(length = as.vector(length)) %>% 
  filter(length > 100) %>% 
  # Add a unique ID for each edge
  mutate(edge_id = seq(1,nrow(.)),
         is_walkable = "unwalkable")

# Merge the two
edges <- bind_rows(edges_1, edges_2)
# =========== NO MODIFY ZONE ENDS HERE ========================================

###I decided to focus on Midtown Atlanta as a walkable community, and a more suburban census tract in Dekalb County. From my experience living near midtown, it has a much more robust infrastructure for pedestrians. Additionally, it has a higher density populaiton, as well as a larger number of POIs. Dekalb county on the other hand, is much more residentia, meaning individuals live on larger areas of land, are not as densely populated.

Step 2. Define getAzimuth() function.

getAzimuth <- function(line){
  
  start_p <- line %>%
    st_coordinates() %>%
    .[1:2,1:2]
  
  start_azi <-atan2(start_p[2, "X"] - start_p[1, "X"],
                   start_p[2, "Y"] - start_p[1, "Y"])*180/pi
  end_p <- line %>%
    st_coordinates() %>%
    .[(nrow(.)-1):nrow(.),1:2]
  
  end_azi <- atan2(end_p[2, "X"] - end_p[1, "X"],
                   end_p[2, "Y"] - end_p[1, "Y"])*180/pi
  
  end_azi <- if (end_azi < 180) {end_azi + 180} else {end_azi - 180}
  
  mid_p <- line %>% 
    st_line_sample(sample = c(0.45, 0.55)) %>%
    st_cast("POINT") %>%
    st_coordinates()
  
  mid_azi <- atan2(mid_p[2, "X"] - mid_p[1, "X"],
                   mid_p[2, "Y"] - mid_p[1, "Y"])*180/pi
  mid_p <- line %>%
    st_line_sample(sample = 0.5) %>%
    st_coordinates %>%
    .[1, 1:2]
  # =========== NO MODIFICATION ZONE STARTS HERE ===============================
  return(tribble(
    ~type,    ~X,            ~Y,             ~azi,
    "start",   start_p[1,"X"], start_p[1,"Y"], start_azi,
    "mid1",    mid_p["X"],   mid_p["Y"],   mid_azi,
    "mid2",    mid_p["X"],   mid_p["Y"],   ifelse(mid_azi < 180, mid_azi + 180, mid_azi - 180),
    "end",     end_p[2,"X"],   end_p[2,"Y"],   end_azi))
  # =========== NO MODIFY ZONE ENDS HERE ========================================
}

Step 3. Apply the function to all street segments

We can apply getAzimuth() function to the edges object. We finally append edges object to make use of the columns in edges object (e.g., is_walkable column). When you are finished with this code chunk, you will be ready to download GSV images.

edges_azi <- edges %>%
  st_geometry() %>%
  map_df(getAzimuth)
  # **YOUR CODE HERE..**

# //TASK //////////////////////////////////////////////////////////////////////

# =========== NO MODIFICATION ZONE STARTS HERE ===============================
edges_azi <- edges_azi %>% 
  bind_cols(edges %>% 
              st_drop_geometry() %>% 
              slice(rep(1:nrow(edges),each=4))) %>% 
  st_as_sf(coords = c("X", "Y"), crs = 4326, remove=FALSE) %>% 
  mutate(node_id = seq(1, nrow(.)))
# =========== NO MODIFY ZONE ENDS HERE ========================================

Step 4. Define a function that formats request URL and download images.

getImage <- function(iterrow){
  # This function takes one row of edges_azi and downloads GSV image using the information from edges_azi.
  
  # TASK ////////////////////////////////////////////////////////////////////////
  # Finish this function definition.
  # 1. Extract required information from the row of edges_azi, including 
  #    type (i.e., start, mid1, mid2, end), location, heading, edge_id, node_id, and key.
  # 2. Format the full URL and store it in `request`. Refer to this page: https://developers.google.com/maps/documentation/streetview/request-streetview
  # 3. Format the full path (including the file name) of the image being downloaded and store it in `fpath`
  type <- iterrow$type
  location <- paste0(iterrow$Y, ",", iterrow$X)
  heading <- iterrow$azi
  edge_id <- iterrow$edge_id
  node_id <- iterrow$node_id
  key <- Sys.getenv("GOOGLE_API_KEY")
  
  endpoint <- "https://maps.googleapis.com/maps/api/streetview"
  
  furl <- glue::glue("{endpoint}?size=640x640&location={location}&heading={heading}&fov=90&pitch=0&key={key}")
  fname <- glue::glue("GSV-nid_{node_id}-eid_{edge_id}-type_{type}-Location_{location}-heading_{heading}.jpg") # Don't change this code for fname
  fpath <- paste0("images/", edge_id, "_", node_id, "_", type, ".jpg")
  # //TASK //////////////////////////////////////////////////////////////////////

  
  
  # =========== NO MODIFICATION ZONE STARTS HERE ===============================
  # Download images
  if (!file.exists(fpath)){
    download.file(furl, fpath, mode = 'wb') 
  }
  # =========== NO MODIFY ZONE ENDS HERE ========================================
}

Step 5. Download GSV images

Before you download GSV images, make sure the row number of edges_azi is not too large! The row number of edges_azi will be the number of GSV images you will be downloading. Before you download images, always double-check your Google Cloud Console’s Billing tab to make sure that you will not go above the free credit of $200 each month. The price is $7 per 1000 images.

# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Loop!
for (i in seq(1,nrow(edges_azi))){
  getImage(edges_azi[i,])
}
# =========== NO MODIFY ZONE ENDS HERE ========================================

ZIP THE DOWNLOADED IMAGES AND NAME IT ‘gsv_images.zip’ FOR STEP 6.

Step 6. Apply computer vision

Now, use Google Colab to apply the semantic segmentation model. Zip your images and upload the images to your Colab session.

Step 7. Merging the processed data back to R

Once all of the images are processed and saved in your Colab session as a CSV file, download the CSV file and merge it back to edges.

# TASK ////////////////////////////////////////////////////////////////////////
# Read the downloaded CSV file from Google Colab
seg_output <- read.csv("seg_output.csv")
# //TASK ////////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Join the seg_output object back to edges_azi object using node_id as the join key.
edges_seg_output <- edges_azi %>%
  inner_join(seg_output, by=c("node_id" = "img_id")) %>%
  select(type, X, Y, node_id, building, sky, tree, road, sidewalk, is_walkable) %>%
  mutate(across(c(building, sky, tree, road, sidewalk), function(x) x/(640*640)))
# =========== NO MODIFY ZONE ENDS HERE ========================================

Section 3. Summarise and analyze the results.

At the beginning of this assignment, you defined one Census Tract as walkable and the other as unwalkable. The key to the following analysis is the comparison between walkable and unwalkable Census Tracts.

Analysis 1 - Create interactive map(s) to visualize the spatial distribution of the streetscape.

You need to create maps of the proportion of building, sky, tree, road, and sidewalk for walkable and unwalkable areas. In total, you will have 10 maps.

Provide a brief description of your findings from the maps.

# TASK ////////////////////////////////////////////////////////////////////////
# Create interactive map(s) to visualize the `edges_seg_output` objects. 
# As long as you can deliver the message clearly, you can use any format/package you want.
if(st_crs(edges_seg_output)$epsg != 4326) { edges_seg_output <- st_transform(edges_seg_output, crs = 4326)}

buildings_walk <- tm_shape(edges_seg_output %>% filter(is_walkable == "walkable")) + tm_dots(fill = "building", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4, 0.5), values = "red"))

sky_walk <- tm_shape(edges_seg_output %>% filter(is_walkable == "walkable")) + tm_dots(fill = "sky", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1), values = "blue"))

tree_walk <- tm_shape(edges_seg_output %>% filter(is_walkable == "walkable")) + tm_dots(fill = "tree", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 07), values = "green"))

road_walk <- tm_shape(edges_seg_output %>% filter(is_walkable == "walkable")) + tm_dots(fill = "road", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4, 0.5), values = "purple"))

sidewalk_walk <- tm_shape(edges_seg_output %>% filter(edges_seg_output$is_walkable == "walkable")) + tm_dots(fill = "sidewalk", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4), values = "brown"))

building_nowalk <- tm_shape(edges_seg_output %>% filter(edges_seg_output$is_walkable == "unwalkable")) + tm_dots(fill = "building", col = "black", size = 0.4, fill.scale = tm_scale_intervals(breaks = c(0.1, 0.2, 0.3, 0.4, 0.5), values = "red"))

building_nowalk
## Warning: Values have found that are less than the lowest break. They are
## assigned to the lowest interval

# //TASK //////////////////////////////////////////////////////////////////////

Analysis 2 - Compare the means.

You need to calculate the mean of the proportion of building, sky, tree, road, and sidewalk for walkable and unwalkable areas. For example, you need to calculate the mean of building category for each of walkable and unwalkable Census Tracts. Then, you need to calculate the mean of sky category for each of walkable and unwalkable Census Tracts. In total, you will have 10 mean values. Provide a brief description of your findings.

# TASK ////////////////////////////////////////////////////////////////////////
# Perform the calculation as described above.
# As long as you can deliver the message clearly, you can use any format/package you want.


# //TASK //////////////////////////////////////////////////////////////////////

Analysis 3 - Draw boxplot

You need to calculate the mean of the proportion of building, sky, tree, road, and sidewalk for walkable and unwalkable areas. For example, you need to calculate the mean of building category for each of walkable and unwalkable Census Tracts. Then, you need to calculate the mean of sky category for each of walkable and unwalkable Census Tracts. In total, you will have 10 mean values. Provide a brief description of your findings.

# TASK ////////////////////////////////////////////////////////////////////////
# Create boxplot(s) using ggplot2 package.


# //TASK //////////////////////////////////////////////////////////////////////