In the first section, you need to select one Census Tract that you think is the most walkable and another one that you think is least walkable within Fulton and DeKalb Counties, GA. As long as the two Census Tracts are within the two counties, you can pick any two you want. If the area you want to use as walkable/unwalkable area is not well-covered by one Census Tract, you can select multiple tracts (e.g., selecting three adjacent tracts as one walkable area). The definition of ‘walkable’ can be your own - you can choose solely based on your experience (e.g., had best/worst walking experience), refer to Walk Score, or any other mix of criteria you want. After you make the selection, provide a short write-up with a map explaining why you chose those Census Tracts.
The second section is the main part of this assignment in which you prepare OSM data, download GSV images, apply computer vision (i.e., semantic segmentation).
In the third section, you will summarise and analyze the output and provide your findings. After you apply computer vision to the images, you will have the number of pixels in each image that represent 150 categories in your data. You will focus on the following categories in your analysis: building, sky, tree, road, and sidewalk. Specifically, you will (1) create maps to visualize the spatial distribution of different elements, (2) compare the mean of each category between the two Census Tract and (3) draw box plots to compare the distributions.
library(tidyverse)
library(osmdata)
library(sfnetworks)
library(units)
library(sf)
library(tidygraph)
library(tmap)
library(here)
library(progress)
library(tidycensus)
library(tmap)
library(magrittr)
if (!require("nominatimlite")) install.packages("nominatimlite")
ttm()
Using Census Reporter, I selected walkable Census Tract(s) and unwalkable Census Tract(s) within Fulton and DeKalb counties. - walkable: I combined Little 5 points, Inman Park (30) & Euclid (204) - unwalkable: I combined Chupp rd. (233.20) & Collinsville (233.17)
I used instinct to separate these tracts, knowing that the presence of sidewalks and the proximity to businesses is very different in these areas. The walkable area is in an arts district near at least two MARTA stations walking distance, while the unwalkable area only has one bus pass it and is not walkable to any non-industrial businesses - you must cross a highway.
My hypothesis what that there would be disparity in the presence of sidewalks between these tracts.
In actuality, gathering the tracts is also executed in Section 2.
# TASK ////////////////////////////////////////////////////////////////////////
# 1. Set up your api key here
tidycensus::census_api_key(Sys.getenv("census_api"))
## 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 = 2020,
state = "GA",
county = c("Fulton", "DeKalb"),
geometry = TRUE)
## Getting data from the 2016-2020 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%
|
|== | 3%
|
|== | 4%
|
|=== | 4%
|
|=== | 5%
|
|==== | 6%
|
|===== | 7%
|
|===== | 8%
|
|====== | 8%
|
|====== | 9%
|
|======= | 10%
|
|======= | 11%
|
|======== | 11%
|
|======== | 12%
|
|========= | 13%
|
|========== | 14%
|
|========== | 15%
|
|=========== | 15%
|
|=========== | 16%
|
|============ | 17%
|
|============= | 18%
|
|============= | 19%
|
|============== | 20%
|
|=============== | 21%
|
|=============== | 22%
|
|================ | 22%
|
|================ | 23%
|
|================= | 24%
|
|================== | 25%
|
|================== | 26%
|
|====================== | 31%
|
|======================= | 32%
|
|======================= | 33%
|
|========================= | 36%
|
|========================== | 37%
|
|========================== | 38%
|
|=========================== | 38%
|
|============================ | 40%
|
|============================= | 41%
|
|============================= | 42%
|
|============================== | 43%
|
|=============================== | 44%
|
|=============================== | 45%
|
|================================ | 45%
|
|================================ | 46%
|
|================================= | 47%
|
|================================= | 48%
|
|================================== | 48%
|
|================================== | 49%
|
|=================================== | 50%
|
|==================================== | 51%
|
|==================================== | 52%
|
|===================================== | 52%
|
|====================================== | 55%
|
|======================================= | 56%
|
|======================================== | 57%
|
|========================================= | 58%
|
|========================================= | 59%
|
|========================================== | 59%
|
|========================================== | 60%
|
|=========================================== | 61%
|
|=========================================== | 62%
|
|============================================ | 63%
|
|============================================= | 64%
|
|============================================== | 65%
|
|============================================== | 66%
|
|=============================================== | 67%
|
|=============================================== | 68%
|
|================================================ | 68%
|
|================================================ | 69%
|
|================================================= | 70%
|
|================================================= | 71%
|
|================================================== | 71%
|
|================================================== | 72%
|
|=================================================== | 73%
|
|==================================================== | 74%
|
|==================================================== | 75%
|
|===================================================== | 75%
|
|===================================================== | 76%
|
|====================================================== | 77%
|
|====================================================== | 78%
|
|======================================================= | 78%
|
|======================================================= | 79%
|
|======================================================== | 80%
|
|========================================================= | 81%
|
|========================================================= | 82%
|
|========================================================== | 83%
|
|=========================================================== | 84%
|
|=========================================================== | 85%
|
|============================================================ | 85%
|
|============================================================ | 86%
|
|============================================================= | 87%
|
|============================================================== | 88%
|
|=============================================================== | 89%
|
|=============================================================== | 90%
|
|================================================================ | 91%
|
|================================================================ | 92%
|
|================================================================= | 92%
|
|================================================================= | 93%
|
|================================================================== | 94%
|
|=================================================================== | 95%
|
|=================================================================== | 96%
|
|==================================================================== | 96%
|
|===================================================================== | 98%
|
|===================================================================== | 99%
|
|======================================================================| 100%
# =========== NO MODIFY ZONE ENDS HERE ========================================
## Just a personal test
# tmap_mode("view")
# tm_shape(tract) + tm_borders() + tm_shape(tract) + tm_borders(col = 'red')
# 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.
# 5. Change the coordinate system to GCS, if necessary.
epsg_id <- 4326
# For the walkable Census Tract(s)
# 1.
tr1_ID <- c("13089020400", "13121003000") # Little 5 points, Inman Park (30) & Euclid (204) # **YOUR CODE HERE..** --> For example, tr1_ID <- c("13121001205", "13121001206").
# 2~4
tract_1_bb <- tract %>%
filter(GEOID %in% tr1_ID) %>% # Use filter instead of st_bbox
st_bbox() # Get bounding box directly
# **YOUR CODE HERE..**
# For the unwalkable Census Tract(s)
# 1.
tr2_ID <- c("13089023320", "13089023317") # Chupp rd. (233.20) & collinsville (233.17) **YOUR CODE HERE..**
# 2~4
tract_2_bb <- tract %>%
filter(GEOID %in% tr2_ID) %>% # Use filter instead of st_bbox
st_bbox() # Get bounding box directly
# **YOUR CODE HERE..**
# //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.
# TASK 1
# walkable
net1 <- osm_1$osm_lines %>%
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
# **YOUR CODE HERE..**
# unwalkable
net2 <- osm_2$osm_lines %>%
# Convert the OSM line to sfnetworks and clean it.
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
# **YOUR CODE HERE..**
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# OSM for the walkable part
edges_1 <- net1 %>%
# Extract 'edges'
st_as_sf("edges") %>%
# Drop redundant columns
select(osm_id, highway, length) %>%
# 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 redundant columns
select(osm_id, highway, length) %>%
# 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 ========================================
getAzi <- function(line){
# This function takes one edge (i.e., a street segment) as an input and
# outputs a data frame with four points (start, mid1, mid2, and end) and their azimuth.
# TASK ////////////////////////////////////////////////////////////////////////
# 1. From `line` object, extract the coordinates using st_coordinates() and extract the first two rows.
# 2. Use atan2() function to calculate the azimuth in degree.
# Make sure to adjust the value such that 0 is north, 90 is east, 180 is south, and 270 is west.
# 1
start_p <- line %>%
st_coordinates() %>%
.[1:2,1:2]
# **YOUR CODE HERE..**
# 2
start_azi <- atan2(start_p[2,"X"] - start_p[1, "X"],
start_p[2,"Y"] - start_p[1, "Y"])*180/pi
# **YOUR CODE HERE..** --> For example, atan2()..
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# Repeat what you did above, but for last two rows (instead of the first two rows).
# Remember to flip the azimuth so that the camera would be looking at the street that's being measured
end_p <- line %>%
st_coordinates() %>%
.[(nrow(.)-1):nrow(.),1:2]
# **YOUR CODE HERE..**
end_azi <- atan2(end_p[2,"X"] - end_p[1, "X"],
end_p[2,"Y"] - end_p[1, "Y"])*180/pi
# **YOUR CODE HERE..** --> For example, atan2()..
end_azi <- if (end_azi < 180) {end_azi + 180} else {end_azi - 180}
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# 1. From `line` object, use st_line_sample() function to generate points at 45%, 50% and 55% locations.
# 2. Use st_cast() function to convert 'MULTIPOINT' object to 'POINT' object.
# 3. Extract coordinates using st_coordinates().
# 4. Use the 50% location to define `mid_p` object.
# 5. Use the 45% and 55% points and atan2() function to calculate azimuth `mid_azi`.
mid_p3 <- line %>%
#.[[1]] %>%
st_line_sample(sample = c(0.45, 0.5, 0.55)) %>%
st_cast("POINT") %>%
st_coordinates()
# **YOUR CODE HERE..**
mid_p <- mid_p3[2,] # **YOUR CODE HERE..**
mid_azi <- atan2(mid_p3[3,"X"] - mid_p3[1, "X"],
mid_p3[3,"Y"] - mid_p3[1, "Y"])*180/pi
# **YOUR CODE HERE..** For example, atan2()..
mid_azi2 <- ifelse(mid_azi < 180, mid_azi + 180, mid_azi - 180)
# //TASK //////////////////////////////////////////////////////////////////////
# =========== 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"], mid_azi2,
"end", end_p[2,"X"], end_p[2,"Y"], end_azi))
# =========== NO MODIFY ZONE ENDS HERE ========================================
}
Apply getAzi() function to the edges object. Finally,
append edges object to make use of the columns in
edges object (e.g., is_walkable column). That
prepares this code chunk to download GSV images.
# TASK ////////////////////////////////////////////////////////////////////////
# Apply getAzi() function to all edges.
# Remember that you need to pass edges object to st_geometry() before you apply getAzi()
endp_azi <- edges %>%
st_geometry() %>%
map_df(getAzi, .progress = T)
# **YOUR CODE HERE..**
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
endp <- endp_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 ========================================
get_image <- function(iterrow){
# This function takes one row of endp and downloads GSV image using the information from endp.
# TASK ////////////////////////////////////////////////////////////////////////
# Finish this function definition.
# 1. Extract required information from the row of endp, including
# type (i.e., start, mid1, mid2, end), location, heading, edge_id, node_id, source (i.e., outdoor vs. default) and key.
# 2. Format the full URL and store it in furl.
# 3. Format the full path (including the file name) of the image being downloaded and store it in fpath
type <- iterrow$type # **YOUR CODE HERE..**
location <- paste0(iterrow$Y %>% round(5), ",", iterrow$X %>% round(5)) # **YOUR CODE HERE..**
heading <- iterrow$azi %>% round(1) # **YOUR CODE HERE..**
edge_id <- iterrow$edge_id # **YOUR CODE HERE..**
node_id <- iterrow$node_id # **YOUR CODE HERE..**
key <- Sys.getenv("google_api") # **YOUR CODE HERE..**
furl <- glue::glue("https://maps.googleapis.com/maps/api/streetview?size=640x640&location={location}&heading={heading}&fov=90&pitch=0&key={key}") # **YOUR CODE HERE..**
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 <- here("downloaded_image", fname) # **YOUR CODE HERE..**
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Download images
if (!file.exists(fpath)){
download.file(furl, fpath, mode = 'wb')
}
# =========== NO MODIFY ZONE ENDS HERE ========================================
}
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Loop!
for (i in seq(1,nrow(endp))){
get_image(endp[i,])
}
# =========== NO MODIFY ZONE ENDS HERE ========================================
Zipped the downloaded images to process in Colab.
Using Google Colab to apply the semantic segmentation model:
Merge the segmentation output to edges.
# Read the downloaded CSV file from Google Drive
seg_output <- read.csv("~/major_assignment_3/seg_output.csv")
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Join the segmentation result to endp object.
seg_output_nodes <- endp %>% inner_join(seg_output, by=c("node_id"="img_id")) %>%
select(type, X, Y, node_id, building, sky, tree, road, sidewalk) %>%
mutate(across(c(building, sky, tree, road, sidewalk), function(x) x/(640*640)))
# =========== NO MODIFY ZONE ENDS HERE ========================================
The key to the following analysis is the comparison between walkable/unwalkable Census Tracts.
The maps are of the proportion of building, sky, tree, road, and sidewalk for walkable, and then unwalkable areas.
# TASK ////////////////////////////////////////////////////////////////////////
# Create map(s) to visualize the `pspnet_nodes` objects.
# As long as you can deliver the message clearly, you can use any format/package you want.
## ---------------------------------------------------------------
install.packages("mapview")
## Installing package into '/usr/local/lib/R/site-library'
## (as 'lib' is unspecified)
library(mapview)
library(nngeo)
# Spatial join
test_join <- seg_output_nodes %>%
st_join(edges %>%
select(is_walkable),
join = nngeo::st_nn, maxdist = 10)
## lines or polygons
##
|
| | 0%
|
| | 1%
|
|= | 1%
|
|= | 2%
|
|== | 2%
|
|== | 3%
|
|== | 4%
|
|=== | 4%
|
|=== | 5%
|
|==== | 5%
|
|==== | 6%
|
|===== | 6%
|
|===== | 7%
|
|===== | 8%
|
|====== | 8%
|
|====== | 9%
|
|======= | 9%
|
|======= | 10%
|
|======= | 11%
|
|======== | 11%
|
|======== | 12%
|
|========= | 12%
|
|========= | 13%
|
|========= | 14%
|
|========== | 14%
|
|========== | 15%
|
|=========== | 15%
|
|=========== | 16%
|
|============ | 16%
|
|============ | 17%
|
|============ | 18%
|
|============= | 18%
|
|============= | 19%
|
|============== | 19%
|
|============== | 20%
|
|============== | 21%
|
|=============== | 21%
|
|=============== | 22%
|
|================ | 22%
|
|================ | 23%
|
|================ | 24%
|
|================= | 24%
|
|================= | 25%
|
|================== | 25%
|
|================== | 26%
|
|=================== | 26%
|
|=================== | 27%
|
|=================== | 28%
|
|==================== | 28%
|
|==================== | 29%
|
|===================== | 29%
|
|===================== | 30%
|
|===================== | 31%
|
|====================== | 31%
|
|====================== | 32%
|
|======================= | 32%
|
|======================= | 33%
|
|======================= | 34%
|
|======================== | 34%
|
|======================== | 35%
|
|========================= | 35%
|
|========================= | 36%
|
|========================== | 36%
|
|========================== | 37%
|
|========================== | 38%
|
|=========================== | 38%
|
|=========================== | 39%
|
|============================ | 39%
|
|============================ | 40%
|
|============================ | 41%
|
|============================= | 41%
|
|============================= | 42%
|
|============================== | 42%
|
|============================== | 43%
|
|============================== | 44%
|
|=============================== | 44%
|
|=============================== | 45%
|
|================================ | 45%
|
|================================ | 46%
|
|================================= | 46%
|
|================================= | 47%
|
|================================= | 48%
|
|================================== | 48%
|
|================================== | 49%
|
|=================================== | 49%
|
|=================================== | 50%
|
|=================================== | 51%
|
|==================================== | 51%
|
|==================================== | 52%
|
|===================================== | 52%
|
|===================================== | 53%
|
|===================================== | 54%
|
|====================================== | 54%
|
|====================================== | 55%
|
|======================================= | 55%
|
|======================================= | 56%
|
|======================================== | 56%
|
|======================================== | 57%
|
|======================================== | 58%
|
|========================================= | 58%
|
|========================================= | 59%
|
|========================================== | 59%
|
|========================================== | 60%
|
|========================================== | 61%
|
|=========================================== | 61%
|
|=========================================== | 62%
|
|============================================ | 62%
|
|============================================ | 63%
|
|============================================ | 64%
|
|============================================= | 64%
|
|============================================= | 65%
|
|============================================== | 65%
|
|============================================== | 66%
|
|=============================================== | 66%
|
|=============================================== | 67%
|
|=============================================== | 68%
|
|================================================ | 68%
|
|================================================ | 69%
|
|================================================= | 69%
|
|================================================= | 70%
|
|================================================= | 71%
|
|================================================== | 71%
|
|================================================== | 72%
|
|=================================================== | 72%
|
|=================================================== | 73%
|
|=================================================== | 74%
|
|==================================================== | 74%
|
|==================================================== | 75%
|
|===================================================== | 75%
|
|===================================================== | 76%
|
|====================================================== | 76%
|
|====================================================== | 77%
|
|====================================================== | 78%
|
|======================================================= | 78%
|
|======================================================= | 79%
|
|======================================================== | 79%
|
|======================================================== | 80%
|
|======================================================== | 81%
|
|========================================================= | 81%
|
|========================================================= | 82%
|
|========================================================== | 82%
|
|========================================================== | 83%
|
|========================================================== | 84%
|
|=========================================================== | 84%
|
|=========================================================== | 85%
|
|============================================================ | 85%
|
|============================================================ | 86%
|
|============================================================= | 86%
|
|============================================================= | 87%
|
|============================================================= | 88%
|
|============================================================== | 88%
|
|============================================================== | 89%
|
|=============================================================== | 89%
|
|=============================================================== | 90%
|
|=============================================================== | 91%
|
|================================================================ | 91%
|
|================================================================ | 92%
|
|================================================================= | 92%
|
|================================================================= | 93%
|
|================================================================= | 94%
|
|================================================================== | 94%
|
|================================================================== | 95%
|
|=================================================================== | 95%
|
|=================================================================== | 96%
|
|==================================================================== | 96%
|
|==================================================================== | 97%
|
|==================================================================== | 98%
|
|===================================================================== | 98%
|
|===================================================================== | 99%
|
|======================================================================| 99%
|
|======================================================================| 100%
# test_join %>%
# count(is_walkable)
test_join %>%
ggplot() +
geom_sf(aes(color = is_walkable))
edges_w <- edges %>% filter(is_walkable == "walkable")
edges_uw <- edges %>% filter(is_walkable == "unwalkable")
# split data by walkability
seg_output_nodes_w <- test_join %>%
filter(is_walkable == "walkable")
seg_output_nodes_uw <- test_join %>%
filter(is_walkable == "unwalkable")
## BUILDING
M1 <- mapview(edges_w) +
mapview(seg_output_nodes_w, zcol = "building")
# legend = mapviewGetOption("Walkable buildings")
M2 <- mapview(edges_uw) +
mapview(seg_output_nodes_uw, zcol = "building")
## SKY
M3 <- mapview(edges_w) +
mapview(seg_output_nodes_w, zcol = "sky")
# legend = mapviewGetOption("Walkable buildings")
M4 <- mapview(edges_uw) +
mapview(seg_output_nodes_uw, zcol = "sky")
## TREE
M5 <- mapview(edges_w) +
mapview(seg_output_nodes_w, zcol = "tree")
# legend = mapviewGetOption("Walkable buildings")
M6 <- mapview(edges_uw) +
mapview(seg_output_nodes_uw, zcol = "tree")
## ROAD
M7 <- mapview(edges_w) +
mapview(seg_output_nodes_w, zcol = "road")
# legend = mapviewGetOption("Walkable buildings")
M8 <- mapview(edges_uw) +
mapview(seg_output_nodes_uw, zcol = "road")
## SIDEWALK
M9 <- mapview(edges_w) +
mapview(seg_output_nodes_w, zcol = "sidewalk")
# legend = mapviewGetOption("Walkable buildings")
M10 <- mapview(edges_uw) +
mapview(seg_output_nodes_uw, zcol = "sidewalk")
# View maps
library(leafsync)
sync(M1, M2, sync = "none")
sync(M3, M4, sync = "none")
sync(M5, M6, sync = "none")
sync(M7, M8, sync = "none")
sync(M9, M10, sync = "none")
# //TASK //////////////////////////////////////////////////////////////////////
Buildings
The data represents that the unwalkable tract represents more sparse
spread of buildings. Compared to the walkable tract, the unwalkable
tract has more of it represented with nodes that are the farthest
proximity from a building. Walkable node_id 1075, you’ll see it’s yellow
and the path seemingly cuts through a building or structure by design.
If you zoom in on unwalkable node_id 2559, you’ll see the path is purple
and so are the others in the area. You can see a mall there and some
shopping centers quite a distance from the path. This is consistent with
the walkability I know about this mall in real life - it’s not very
accessible by foot.
Sky
Zooming in on the tracts, the unwalkable data seems to have more
proximity to the sky. This could make sense, if the buildings are more
sparse or one-story. There’s not enough data here to compare building
heights. However, more of the unwalkable tract seems to be represented
by personal homes (and I happen to know many one-story industrial
businesses but that’s not evident in the data here). What can be
inferred is that less proximity to buildings and an ~equal amount of
roads may present more opportunities for unobstructed skies.
Tree
The unwalkable tract sees dense proximity of trees wherever there is a
lack of buildings along an edge. The walkable tract looks pretty
consistent with moderate tree coverage, no matter what types of
buildings are present. Perhaps this was a priority in the city design,
especially if it’s more walkable in a hot climate.
Roads
The walkable tract has moderate to high proximity to roads throughout.
The unwalkable tract seems to contain more road data that indcates a
farther proximity - this could possibly mean the roads are
private access, especially in an industrial area that also has large
residential single-family home neighborhoods.
Sidewalk
I’m honestly surprised that the walkable tract doesn’t totally align
with assumptions I had about proximity to sidewalks. I thought they’d be
much more abundant than they are, especially in the Little 5 Points
area. However, the data shows low to moderate proximity. (Now that I
think about it, it’s at least an 8 minute walk to either train station,
but I didn’t mind much in the past). The unwalkable tract, on the other
hand, has virtually no proximity to sidewalks in the data at all, and
this aligns with my lived experience of the area - it’s a very
car-centric area, as is supported by this project data.
Calculate the mean of the proportion of building, sky, tree, road, and sidewalk for walkable and unwalkable areas.
# TASK ////////////////////////////////////////////////////////////////////////
# Perform the calculation as described above.
# As long as you can deliver the message clearly, you can use any format/package you want.
install.packages("flextable")
## Installing package into '/usr/local/lib/R/site-library'
## (as 'lib' is unspecified)
library(flextable)
##
## Attaching package: 'flextable'
## The following object is masked from 'package:purrr':
##
## compose
test_join %>%
as.data.frame() %>%
group_by(is_walkable) %>%
summarise(building = mean(building, na.rm = TRUE),
sky = mean(sky, na.rm = TRUE),
tree = mean(tree, na.rm = TRUE),
road = mean(road, na.rm = TRUE),
sidewalk = mean(sidewalk, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, 6)) %>%
flextable() %>%
autofit()
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `across(where(is.numeric), round, 6)`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
is_walkable | building | sky | tree | road | sidewalk |
|---|---|---|---|---|---|
unwalkable | 0.024117 | 0.358355 | 0.179819 | 0.311513 | 0.008990 |
walkable | 0.073736 | 0.249103 | 0.198470 | 0.339241 | 0.028982 |
# //TASK //////////////////////////////////////////////////////////////////////
The walkable tract has higher proximity to buildings, thus higher proximity to sidewalks. Theres significantly lower proximity when it comes to the sky data. This could indicate taller buildings, but that discovery is out of scope for this project. The unwalkable tract has markedly lower proximity to sidewalks, which is a significant indicator of the “unwalkable” evaluation of those nodes from the segmentation analysis.
The disparity of tree and road proximity is not too significant, which aligns with the fact that the counties in the tract are within the city of Atlanta - known to be car-centric and nationally nicknamed “The City in a Forest”.
Comparing the proportion of building, sky, tree, road, and sidewalk between walkable and unwalkable areas.
# TASK ////////////////////////////////////////////////////////////////////////
# Create box plot(s) using geom_boxplot() function from ggplot2 package.
# Use `seg_output_nodes` object to draw the box plots.
# You will find `pivot_longer` function useful.
test_join %>%
as.data.frame() %>%
pivot_longer(cols = (building:sidewalk)) %>%
ggplot(aes(x = is_walkable, y = value)) +
geom_boxplot(aes(color = is_walkable)) +
facet_wrap(~name) +
theme_bw() +
theme(legend.position = c(0.8, 0.2))
# //TASK //////////////////////////////////////////////////////////////////////
Here, while the means do not differ significantly on a lot of the data, the disparity becomes more evident when you look at the outlier plots.
Walkable tract
* higher density in the closer proximity to buildings
* slightly higher median for road proximity
* higher density with the closer proximity to sidewalks
* significantly lower median and outlier with proximity to sky
* significantly higher population of trees above the 50%
Unwalkable tract
* proximity to buildings isn’t significantly lower, but it’s more
densely represented at a lower proximity
* proximity to sidewalks sparser, and is denser at the lower
proximity
* the road proximity is not as disparate as building proximity compared
to the walkable tract