Section 0. Packages

#Import necessary packages
library(tidycensus)
library(dplyr)
library(tmap)
library(tidyverse)
library(sf)
library(osmdata)
library(sfnetworks)
library(tidygraph)
library(units)
library(leaflet)
library(leafsync)
library(ggplot2)

Section 1. Choose your Census Tracts

# TASK ////////////////////////////////////////////////////////////////////////
# Set up your api key here
census_api_key(Sys.getenv("CENSUS_API"))
# //TASK //////////////////////////////////////////////////////////////////////

# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Download Census Tract polygon for Fulton and DeKalb
tract <- get_acs("tract", 
                 variables = c('pop' = 'B01001_001'),
                 year = 2023,
                 state = "GA", 
                 county = c("Fulton", "DeKalb"), 
                 geometry = TRUE)
##   |                                                                              |                                                                      |   0%  |                                                                              |=                                                                     |   1%  |                                                                              |=                                                                     |   2%  |                                                                              |==                                                                    |   2%  |                                                                              |==                                                                    |   3%  |                                                                              |==                                                                    |   4%  |                                                                              |===                                                                   |   4%  |                                                                              |===                                                                   |   5%  |                                                                              |====                                                                  |   5%  |                                                                              |====                                                                  |   6%  |                                                                              |=====                                                                 |   7%  |                                                                              |=====                                                                 |   8%  |                                                                              |======                                                                |   8%  |                                                                              |======                                                                |   9%  |                                                                              |=======                                                               |   9%  |                                                                              |=======                                                               |  10%  |                                                                              |=======                                                               |  11%  |                                                                              |========                                                              |  11%  |                                                                              |========                                                              |  12%  |                                                                              |=========                                                             |  13%  |                                                                              |==========                                                            |  14%  |                                                                              |==========                                                            |  15%  |                                                                              |===========                                                           |  16%  |                                                                              |============                                                          |  17%  |                                                                              |=============                                                         |  18%  |                                                                              |=============                                                         |  19%  |                                                                              |==============                                                        |  19%  |                                                                              |==============                                                        |  20%  |                                                                              |===============                                                       |  21%  |                                                                              |===============                                                       |  22%  |                                                                              |================                                                      |  22%  |                                                                              |================                                                      |  23%  |                                                                              |=================                                                     |  24%  |                                                                              |==================                                                    |  25%  |                                                                              |==================                                                    |  26%  |                                                                              |===================                                                   |  27%  |                                                                              |====================                                                  |  28%  |                                                                              |====================                                                  |  29%  |                                                                              |=====================                                                 |  30%  |                                                                              |======================                                                |  31%  |                                                                              |======================                                                |  32%  |                                                                              |=======================                                               |  33%  |                                                                              |========================                                              |  34%  |                                                                              |========================                                              |  35%  |                                                                              |=========================                                             |  35%  |                                                                              |=========================                                             |  36%  |                                                                              |==========================                                            |  37%  |                                                                              |==========================                                            |  38%  |                                                                              |===========================                                           |  38%  |                                                                              |===========================                                           |  39%  |                                                                              |============================                                          |  40%  |                                                                              |============================                                          |  41%  |                                                                              |=============================                                         |  41%  |                                                                              |=============================                                         |  42%  |                                                                              |==============================                                        |  43%  |                                                                              |===============================                                       |  44%  |                                                                              |================================                                      |  45%  |                                                                              |================================                                      |  46%  |                                                                              |=================================                                     |  47%  |                                                                              |==================================                                    |  48%  |                                                                              |==================================                                    |  49%  |                                                                              |===================================                                   |  49%  |                                                                              |===================================                                   |  50%  |                                                                              |====================================                                  |  51%  |                                                                              |====================================                                  |  52%  |                                                                              |=====================================                                 |  52%  |                                                                              |=====================================                                 |  53%  |                                                                              |======================================                                |  54%  |                                                                              |======================================                                |  55%  |                                                                              |=======================================                               |  55%  |                                                                              |=======================================                               |  56%  |                                                                              |========================================                              |  57%  |                                                                              |========================================                              |  58%  |                                                                              |=========================================                             |  58%  |                                                                              |=========================================                             |  59%  |                                                                              |==========================================                            |  60%  |                                                                              |===========================================                           |  61%  |                                                                              |===========================================                           |  62%  |                                                                              |============================================                          |  63%  |                                                                              |=============================================                         |  64%  |                                                                              |=============================================                         |  65%  |                                                                              |==============================================                        |  66%  |                                                                              |===============================================                       |  67%  |                                                                              |===============================================                       |  68%  |                                                                              |================================================                      |  69%  |                                                                              |=================================================                     |  69%  |                                                                              |=================================================                     |  70%  |                                                                              |==================================================                    |  71%  |                                                                              |==================================================                    |  72%  |                                                                              |===================================================                   |  72%  |                                                                              |===================================================                   |  73%  |                                                                              |====================================================                  |  74%  |                                                                              |=====================================================                 |  75%  |                                                                              |=====================================================                 |  76%  |                                                                              |======================================================                |  77%  |                                                                              |=======================================================               |  78%  |                                                                              |=======================================================               |  79%  |                                                                              |========================================================              |  80%  |                                                                              |=========================================================             |  81%  |                                                                              |=========================================================             |  82%  |                                                                              |==========================================================            |  83%  |                                                                              |===========================================================           |  84%  |                                                                              |===========================================================           |  85%  |                                                                              |============================================================          |  85%  |                                                                              |============================================================          |  86%  |                                                                              |=============================================================         |  87%  |                                                                              |=============================================================         |  88%  |                                                                              |==============================================================        |  88%  |                                                                              |==============================================================        |  89%  |                                                                              |===============================================================       |  90%  |                                                                              |===============================================================       |  91%  |                                                                              |================================================================      |  91%  |                                                                              |================================================================      |  92%  |                                                                              |=================================================================     |  93%  |                                                                              |=================================================================     |  94%  |                                                                              |==================================================================    |  94%  |                                                                              |===================================================================   |  95%  |                                                                              |===================================================================   |  96%  |                                                                              |====================================================================  |  97%  |                                                                              |===================================================================== |  98%  |                                                                              |===================================================================== |  99%  |                                                                              |======================================================================|  99%  |                                                                              |======================================================================| 100%
tmap_mode("view")
tm_basemap("OpenStreetMap") +
  tm_shape(tract) + 
  tm_polygons(fill_alpha = 0.2)
# =========== NO MODIFY ZONE ENDS HERE ========================================
# TASK ////////////////////////////////////////////////////////////////////////
# 1. Specify the GEOIDs of your walkable and unwalkable Census Tracts. 
#    e.g., tr_id_walkable <- c("13121001205", "13121001206")
# 2. Extract the selected Census Tracts using `tr_id_walkable` and `tr_id_unwalkable`

# For the walkable Census Tract(s)
tr_id_walkable <- c("13121001001")

tract_walkable <- tract %>% 
  filter(GEOID == tr_id_walkable)

# For the unwalkable Census Tract(s)
tr_id_unwalkable <- c("13121009601")

tract_unwalkable <- tract %>% 
  filter(GEOID == tr_id_unwalkable)
# //TASK //////////////////////////////////////////////////////////////////////

# TASK ////////////////////////////////////////////////////////////////////////
# Create an interactive map showing `tract_walkable` and `tract_unwalkable`
tmap_mode("view")
tm_basemap("OpenStreetMap") +
  tm_shape(tract) +
  tm_polygons(alpha = 0.2, col = "#adadad") +
  tm_shape(tract_walkable) +
  tm_borders(col = "#38b6f5", lwd = 5) +
  tm_fill(col = "#38b6f5", alpha = 0.6, title = "Most Walkable Tract") +
  tm_shape(tract_unwalkable) +
  tm_borders(col = "#db9d32", lwd = 5) +
  tm_fill(col = "#db9d32", alpha = 0.6, title = "Least Walkable Tract") +
  tm_layout(
    main.title = "Selected Walkable vs Unwalkable Census Tracts",
    main.title.size = 1.2,
  )
# //TASK //////////////////////////////////////////////////////////////////////

Brief Description of Selected Census Tracts

    The census tract I selected as most walkable is Census Tract 10.01, which is located in Midtown. It is most walkable considering its proximity to the Midtown and North Avenue MARTA stations. It is also more walkability because its block size is smaller and there are more crosswalks. The pedestrian sidewalks are also well-maintained.

    The census tract I selected as least walkable is Census Tract 96.01, which is located in Buckhead. It is less walkable because it is so difficult to reach the nearest Buckhead MARTA station. It is very not pedestrian-friendly due to its large block size and poorly maintained sidewalks. The cars that drive by are at dangerous speed that I always feel like I’ll get hit if I’m not being careful.

Section 2. OSM, GSV, and Computer Vision

Step 1. Get and clean OSM data

# TASK ////////////////////////////////////////////////////////////////////////
# Create one bounding box (`tract_walkable_bb`) for your walkable Census Tract(s) and another (`tract_unwalkable_bb`) for your unwalkable Census Tract(s).

# For the walkable Census Tract(s)
tract_walkable_bb <- st_bbox(tract_walkable)

# For the unwalkable Census Tract(s)  
tract_unwalkable_bb <- st_bbox(tract_unwalkable)
# //TASK //////////////////////////////////////////////////////////////////////

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

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

# TASK ////////////////////////////////////////////////////////////////////////
# 1. Convert `osm_walkable` and `osm_unwalkable` into sfnetwork objects (as undirected networks),
# 2. Clean the network by (1) deleting parallel lines and loops, (2) creating missing nodes, and (3) removing pseudo nodes (make sure the `summarise_attributes` argument is set to 'first' when doing so).

net_walkable <- osm_walkable$osm_lines %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  sfnetworks::as_sfnetwork(directed = FALSE) %>%
  activate("edges") %>%
  filter(!edge_is_multiple()) %>% # remove duplicated edges
  filter(!edge_is_loop()) %>% # remove loops
  convert(., sfnetworks::to_spatial_subdivision) %>% # subdivide edges
  convert(., sfnetworks::to_spatial_smooth, summarise_attributes = "first") # delete pseudo nodes

net_unwalkable <- osm_unwalkable$osm_lines %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  sfnetworks::as_sfnetwork(directed = FALSE) %>%
  activate("edges") %>%
  filter(!edge_is_multiple()) %>% # remove duplicated edges
  filter(!edge_is_loop()) %>% # remove loops
  convert(., sfnetworks::to_spatial_subdivision) %>% # subdivide edges
  convert(., sfnetworks::to_spatial_smooth, summarise_attributes = "first") # delete pseudo nodes
# //TASK //////////////////////////////////////////////////////////////////////
  
# TASK //////////////////////////////////////////////////////////////////////
# Using `net_walkable` and`net_unwalkable`,
# 1. Activate the edge component of each network.
# 2. Create a `length` column.
# 3. Filter out short (<300 feet) segments.
# 4. Randomly Sample 100 rows per road type.
# 5. Assign the results to `edges_walkable` and `edges_unwalkable`, respectively.

# OSM for the walkable part
edges_walkable <- net_walkable %>% 
  # Extract 'edges'
  st_as_sf("edges") %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  # Add a length column
  mutate(length = st_length(.) %>% unclass()) %>% 
  # Drop short segments (< 300 feet)
  filter(length >= 91) %>% 
  # Sample 10 rows per road type
  group_by(highway) %>% 
  slice_sample(n = 100) %>% 
  # Assign a unique ID for each edge
  ungroup() %>% 
  mutate(edge_id = seq(1,nrow(.)))

# OSM for the unwalkable part
edges_unwalkable <- net_unwalkable %>% 
  # Extract 'edges'
  st_as_sf("edges") %>% 
  # Drop redundant columns 
  select(osm_id, highway) %>% 
  # Add a length column
  mutate(length = st_length(.) %>% unclass()) %>% 
  # Drop short segments (< 300 feet)
  filter(length >= 91) %>% 
  # Sample 10 rows per road type
  group_by(highway) %>% 
  slice_sample(n = 100) %>% 
  # Assign a unique ID for each edge
  ungroup() %>% 
  mutate(edge_id = seq(1,nrow(.)))
# //TASK //////////////////////////////////////////////////////////////////////
  
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Merge the two
edges <- bind_rows(edges_walkable %>% mutate(is_walkable = TRUE), 
                   edges_unwalkable %>% mutate(is_walkable = FALSE)) %>% 
  mutate(edge_id = seq(1,nrow(.)))
# =========== NO MODIFY ZONE ENDS HERE ========================================

Step 2. Define getAzimuth() function

getAzimuth <- function(line){
  
  # TASK ////////////////////////////////////////////////////////////////////////
  # 1. Use the `st_line_sample()` function to sample three points at locations 0.48, 0.5, and 0.52 along the line. These points will be used to calculate the azimuth.
  # 2. Use `st_cast()` function to convert the 'MULTIPOINT' object into a 'POINT' object.
  # 3. Extract coordinates using `st_coordinates()`.
  # 4. Assign the coordinates of the midpoint to `mid_p`.
  # 5. Calculate the azimuths from the midpoint in both directions and save them as `mid_azi_1` and `mid_azi_2`, respectively.
  
  # 1-3
  mid_p3 <- line %>% 
    st_line_sample(sample = c(0.48, 0.5, 0.52)) %>%
    st_cast("POINT") %>% 
    st_coordinates()
    
  # 4
  mid_p <- mid_p3[2,]
    
  # 5
  mid_azi_1 <- atan2(mid_p3[1,"X"] - mid_p3[2, "X"],
                     mid_p3[1,"Y"] - mid_p3[2, "Y"])*180/pi
  
  mid_azi_2 <- atan2(mid_p3[3,"X"] - mid_p3[2, "X"],
                     mid_p3[3,"Y"] - mid_p3[2, "Y"])*180/pi
  # //TASK //////////////////////////////////////////////////////////////////////
  
  # =========== NO MODIFICATION ZONE STARTS HERE ===============================
  return(tribble(
    ~type,    ~X,            ~Y,             ~azi,
    "mid1",    mid_p["X"],   mid_p["Y"],      mid_azi_1,
    "mid2",    mid_p["X"],   mid_p["Y"],      mid_azi_2))
  # =========== NO MODIFY ZONE ENDS HERE ========================================
}

Step 3. Apply the function to all street segments

# TASK ////////////////////////////////////////////////////////////////////////
# Apply getAzimuth() function to all edges.
# Remember that you need to pass edges object to st_geometry() before you apply getAzimuth()
edges_azi <- edges %>% 
  st_geometry() %>% 
  map_df(getAzimuth, .progress = T)
  # //TASK //////////////////////////////////////////////////////////////////////
  
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
edges_azi <- edges_azi %>% 
  bind_cols(edges %>% 
              st_drop_geometry() %>% 
              slice(rep(1:nrow(edges),each=2))) %>% 
  st_as_sf(coords = c("X", "Y"), crs = 4326, remove=FALSE) %>% 
  mutate(img_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 the row.
  
  # TASK ////////////////////////////////////////////////////////////////////////
  # 1. Extract required information from the row of `edges_azi`
  # 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 %>% round(5), ",", iterrow$X %>% round(5))
  heading <- iterrow$azi %>% round(1)
  edge_id <- iterrow$edge_id
  img_id <- iterrow$img_id
  key <- Sys.getenv("GOOGLE_API") # your Google API key
  
  endpoint <- "https://maps.googleapis.com/maps/api/streetview"
  
  request <- glue::glue("{endpoint}?size=640x640&location={location}&heading={heading}&fov=90&pitch=0&key={key}")
  fname <- glue::glue("GSV-nid_{img_id}-eid_{edge_id}-type_{type}-Location_{location}-heading_{heading}.jpg") # Don't change this code for fname
  fpath <- file.path("M:/CP8883_UA/Assignments/Major/Major2/GSVImages", fname)
  # //TASK //////////////////////////////////////////////////////////////////////

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

Step 5. Download GSV images

# =========== NO MODIFICATION ZONE STARTS HERE ===============================
for (i in seq(1,nrow(edges_azi))){
  getImage(edges_azi[i,])
}
#ZIP THE DOWNLOADED IMAGES AND NAME IT 'gsv_images.zip' FOR STEP 6.
# =========== NO MODIFY ZONE ENDS HERE ========================================

Step 6. Apply computer vision

Note: Done in Google Colab

Step 7. Merging the processed data back to R

# TASK ////////////////////////////////////////////////////////////////////////
# Read the downloaded CSV file containing the semantic segmentation results.
seg_output <- read.csv("seg_output.csv")
# //TASK ////////////////////////////////////////////////////////////////////////
  
# TASK ////////////////////////////////////////////////////////////////////////  
# 1. Join the `seg_output` data to `edges_azi`.
# 2. Calculate the proportion of predicted pixels for the following categories: `building`, `sky`, `road`, and `sidewalk`. If there are other categories you are interested in, feel free to include their proportions as well.
# 3. Calculate the proportion of greenness using the `vegetation` and `terrain` categories.
# 4. Calculate the building-to-street ratio. For the street, use `road` and `sidewalk` pixels; including `car` pixels is optional.

edges_seg_output <- edges_azi %>%
  inner_join(seg_output, by=c("img_id"="img_id")) %>%
  mutate(p_building = building/(768*768),
         p_sky = sky/(768*768),
         p_road = road/(768*768),
         p_sidewalk = sidewalk/(768*768),
         p_greenness = (vegetation + terrain)/(768*768),
         b2s_ratio = building / (road + sidewalk + car)) %>%
  mutate(b2s_ratio = case_when(
    b2s_ratio >= 1 ~ 1, # Set the upper limit as 1.
    TRUE ~ b2s_ratio))
# //TASK ////////////////////////////////////////////////////////////////////////

Section 3. Summarize and analyze the results

Analysis 1 - Visualize Spatial Distribution

# TASK ////////////////////////////////////////////////////////////////////////
# Plot interactive map(s)
# As long as you can deliver the message clearly, you can use any format/package you want.

walkable_edges   <- edges_seg_output %>% filter(is_walkable == TRUE)
unwalkable_edges <- edges_seg_output %>% filter(is_walkable == FALSE)

# 3 Walkable maps
m1_walk <- leaflet(walkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("Blues", p_sidewalk)(p_sidewalk),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Sidewalk Proportion: ", round(p_sidewalk, 2))
  ) %>%
  addLegend(pal = colorNumeric("Blues", walkable_edges$p_sidewalk),
            values = ~p_sidewalk, title = "Sidewalk Proportion")

m2_walk <- leaflet(walkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("YlGn", p_greenness)(p_greenness),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Greenness Proportion: ", round(p_greenness, 2))
  ) %>%
  addLegend(pal = colorNumeric("YlGn", walkable_edges$p_greenness),
            values = ~p_greenness, title = "Greenness Proportion")

m3_walk <- leaflet(walkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("YlOrBr", b2s_ratio)(b2s_ratio),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Building-to-Street Ratio: ", round(b2s_ratio, 2))
  ) %>%
  addLegend(pal = colorNumeric("YlOrBr", walkable_edges$b2s_ratio),
            values = ~b2s_ratio, title = "Building–to-Street Ratio")

# 3 Unwalkable maps
m1_unwalk <- leaflet(unwalkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("Blues", p_sidewalk)(p_sidewalk),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Sidewalk Proportion: ", round(p_sidewalk, 2))
  ) %>%
  addLegend(pal = colorNumeric("Blues", unwalkable_edges$p_sidewalk),
            values = ~p_sidewalk, title = "Sidewalk Proportion")

m2_unwalk <- leaflet(unwalkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("YlGn", p_greenness)(p_greenness),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Greenness Proportion: ", round(p_greenness, 2))
  ) %>%
  addLegend(pal = colorNumeric("YlGn", unwalkable_edges$p_greenness),
            values = ~p_greenness, title = "Greenness Proportion")

m3_unwalk <- leaflet(unwalkable_edges) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    color = ~colorNumeric("YlOrBr", b2s_ratio)(b2s_ratio),
    radius = 3,
    opacity = 0.9,
    fillOpacity = 0.8,
    label = ~paste0("Building-to-Street Ratio: ", round(b2s_ratio, 2))
  ) %>%
  addLegend(pal = colorNumeric("YlOrBr", unwalkable_edges$b2s_ratio),
            values = ~b2s_ratio, title = "Building–to-Street Ratio")
# //TASK //////////////////////////////////////////////////////////////////////

Sidewalk Proportion

leafsync::sync(m1_walk, m1_unwalk)

    From the interactive map comparing sidewalk proportion between Midtown and Buckhead, we can see that there are way more points due to more road segments within Midtown than Buckhead. Generally speaking, the points in Midtown have more darker colors, indicating more segments have higher sidewalk proportions.

Greenness Proportion

leafsync::sync(m2_walk, m2_unwalk) 

    From the interactive map comparing greenness proportion between Midtown and Buckhead, we can see that majority of points in Buckhead have higher greenness, compared to Midtown.

Building-to-Street Ratio

leafsync::sync(m3_walk, m3_unwalk) 

    From the interactive map comparing building-to-street ratio between Midtown and Buckhead, we can see significantly higher ratio in Midtown compared to Buckhead, indicating that there is a denser environment in Midtown.

Analysis 2 - Boxplot

# TASK ////////////////////////////////////////////////////////////////////////
# Create boxplot(s) using ggplot2 package.
edges_seg_output$walkable <- if_else(edges_seg_output$is_walkable == TRUE, "walkable", "unwalkable")
# //TASK //////////////////////////////////////////////////////////////////////

Building Proportion

ggplot(edges_seg_output, aes(x = walkable, y = p_building)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Building Proportion by Walkability",
    x = NULL,
    y = "Proportion of Building Pixels"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of building proportion, we can see that walkable tracts have higher proportions of building pixels, indicating that there are denser developments in Midtown.

Sky Proportion

ggplot(edges_seg_output, aes(x = walkable, y = p_sky)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Sky Proportion by Walkability",
    x = NULL,
    y = "Proportion of Sky Pixels"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of sky proportion, we can see that unwalkable tracts have higher proportions of sky pixels, indicating that there are less buildings and more open skies in Buckhead.

Road Proportion

ggplot(edges_seg_output, aes(x = walkable, y = p_road)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Road Proportion by Walkability",
    x = NULL,
    y = "Proportion of Road Pixels"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of road proportion, we can see that walkable and unwalkable tracts have similar proportions of road pixels.

Sidewalk Proportion

ggplot(edges_seg_output, aes(x = walkable, y = p_sidewalk)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Sidewalk Proportion by Walkability",
    x = NULL,
    y = "Proportion of Sidewalk Pixels"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of sidewalk proportion, we can see that walkable tracts have higher proportions of sidewalk pixels, indicating that there are more sidewalks in Midtown thus more pedestrian-friendly.

Greenness Proportion

ggplot(edges_seg_output, aes(x = walkable, y = p_greenness)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Greenness Proportion by Walkability",
    x = NULL,
    y = "Proportion of Greenness Pixels"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of greenness proportion, we can see that unwalkable tracts have higher proportions of greenness, indicating that there are more vegetation along roads in Buckhead.

Building-to-Street Ratio

ggplot(edges_seg_output, aes(x = walkable, y = b2s_ratio)) +
  geom_boxplot(outlier.shape = 21, alpha = 0.8, fill = "#b8b8b8") +
  labs(
    title = "Building-to-Street Ratio by Walkability",
    x = NULL,
    y = "Building-to-Street Ratio"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

    From the boxplot of building-to-street ratio, we can see that walkable tracts have higher ratios, indicating that there are more buildings compared to streets and cars thus more pedestrian-oriented streets in Midtown.

Analysis 3 - Mean Comparison (t-test)

# TASK ////////////////////////////////////////////////////////////////////////
# Perform t-tests and report both the differences in means and their statistical significance.
# As long as you can deliver the message clearly, you can use any format/package you want.

# Create df to store results
t_results <- data.frame(
  variable = character(),
  mean_walkable = numeric(),
  mean_unwalkable = numeric(),
  p_value = numeric(),
  stringsAsFactors = FALSE
)

variables <- vars <- c("p_building", "p_sky", "p_road", "p_sidewalk", "p_greenness", "b2s_ratio")

# For loop to run t-tests for each variable
for (v in variables) {
  
  # Run t-test for walkable vs unwalkable
  ttest <- t.test(
    edges_seg_output[[v]] ~ edges_seg_output$walkable
  )
  
  # Store results
  t_results <- rbind(
    t_results,
    data.frame(
      variable = v,
      mean_walkable = round(mean(edges_seg_output[[v]][edges_seg_output$walkable == "walkable"], na.rm = TRUE),2),
      mean_unwalkable = round(mean(edges_seg_output[[v]][edges_seg_output$walkable == "unwalkable"], na.rm = TRUE),2),
      p_value = round(ttest$p.value,2)
    )
  )
}

t_results
##      variable mean_walkable mean_unwalkable p_value
## 1  p_building          0.15            0.09    0.00
## 2       p_sky          0.16            0.18    0.03
## 3      p_road          0.36            0.37    0.36
## 4  p_sidewalk          0.04            0.03    0.04
## 5 p_greenness          0.24            0.30    0.01
## 6   b2s_ratio          0.36            0.21    0.00
# //TASK //////////////////////////////////////////////////////////////////////

    T-test shows that there are statistically significant (p < 0.05) differences for proportion of building, sky, sidewalk, and greenness, and building-to-street ratio between walkable and unwalkable tracts.