Importing the necessary packages is part of this assignment. Add any required packages to the code chunk below as you progress through the tasks.
library(tidytransit) # read_gtfs(), gtfs_as_sf()
library(gtfsrouter) # gtfs_transfer_table()
## Registered S3 method overwritten by 'gtfsrouter':
## method from
## summary.gtfs gtfsio
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(stringr)
library(sf)
## Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 8.2.1; sf_use_s2() is TRUE
library(dplyr)
library(tidycensus)
library(osmdata)
## Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(sfnetworks)
library(tidygraph)
##
## Attaching package: 'tidygraph'
## The following object is masked from 'package:stats':
##
## filter
library(units)
## udunits database from /usr/share/xml/udunits/udunits2.xml
library(purrr)
library(hms)
library(leaflet)
library(htmltools)
# TASK ////////////////////////////////////////////////////////////////////////
# Download MARTA GTFS data and assign it to `gtfs` object
tmp_zip <- tempfile(fileext = ".zip")
utils::download.file(
url = "https://itsmarta.com/google_transit_feed/google_transit.zip",
destfile = tmp_zip,
mode = "wb",
quiet = TRUE
)
gtfs <- tidytransit::read_gtfs(tmp_zip)
if (exists("stop_group_distances")) rm(stop_group_distances)
## Warning in rm(stop_group_distances): object 'stop_group_distances' not found
stop_group_distances <- function(stops, by = "stop_name") {
# If not sf yet, build sf from lon/lat
if (!inherits(stops, "sf")) {
need <- c(by, "stop_lon", "stop_lat")
if (!all(need %in% names(stops))) {
stop("Expected columns in `stops`: ", paste(need, collapse = ", "))
}
stops <- sf::st_as_sf(
stops,
coords = c("stop_lon", "stop_lat"),
crs = 4326,
remove = FALSE
)
} else {
if (is.na(sf::st_crs(stops))) stops <- sf::st_set_crs(stops, 4326)
}
nm <- by
dplyr::group_by(stops, .data[[nm]]) |>
dplyr::summarise(
dist_max = {
n <- dplyr::n()
if (n <= 1) 0 else {
dm <- sf::st_distance(geometry)
as.numeric(max(dm, na.rm = TRUE))
}
},
.groups = "drop"
)
}
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Edit stop_name to append serial numbers (1, 2, etc.) to remove duplicate names
stop_dist <- stop_group_distances(gtfs$stops, by='stop_name') %>%
filter(dist_max > 200)
gtfs$stops <- gtfs$stops %>%
group_by(stop_name) %>%
mutate(stop_name = case_when(stop_name %in% stop_dist$stop_name ~ paste0(stop_name, " (", seq(1,n()), ")"),
TRUE ~ stop_name))
# Create a transfer table
gtfs <- gtfsrouter::gtfs_transfer_table(gtfs,
d_limit = 200,
min_transfer_time = 120)
gtfs <- gtfs %>% gtfs_as_sf(crs = 4326)
gtfs$stops <- gtfs$stops %>%
ungroup() %>%
mutate(stop_lat = st_coordinates(.)[,2],
stop_lon = st_coordinates(.)[,1])
# Get stop_id for rails and buses
rail_stops <- gtfs$routes %>%
filter(route_type %in% c(1)) %>%
inner_join(gtfs$trips, by = "route_id") %>%
inner_join(gtfs$stop_times, by = "trip_id") %>%
inner_join(gtfs$stops, by = "stop_id") %>%
group_by(stop_id) %>%
slice(1) %>%
pull(stop_id)
# Extract MARTA rail stations
station <- gtfs$stops %>% filter(stop_id %in% rail_stops)
# Extract Midtown Station
midtown <- gtfs$stops %>% filter(stop_id == "134")
bbox <- st_bbox(c(xmin = -84.45241, ymin = 33.72109, xmax = -84.35009, ymax = 33.80101),
crs = st_crs(4326)) %>%
st_as_sfc()
# =========== NO MODIFY ZONE ENDS HERE ========================================
# TASK ////////////////////////////////////////////////////////////////////////
census_api_key("8d6ffb1affac98f6f4cc2a28150fc30546b21861", install = TRUE, overwrite = TRUE)
## Your original .Renviron will be backed up and stored in your R HOME directory if needed.
## Your API key has been stored in your .Renviron and can be accessed by Sys.getenv("CENSUS_API_KEY").
## To use now, restart R or run `readRenviron("~/.Renviron")`
## [1] "8d6ffb1affac98f6f4cc2a28150fc30546b21861"
# Income: Median household income
vars_income <- c(hhinc = "B19013_001")
census_income <- tidycensus::get_acs(
geography = "tract",
variables = vars_income,
year = 2022,
state = "GA",
county = c("Fulton","DeKalb","Clayton"),
geometry = TRUE,
output = "wide"
) |>
dplyr::rename(hhinc = hhincE) |>
dplyr::select(GEOID, NAME, hhinc, geometry)
## 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% | |=== | 5% | |==== | 5% | |==== | 6% | |===== | 7% | |===== | 8% | |====== | 8% | |====== | 9% | |======= | 10% | |======= | 11% | |======== | 11% | |======== | 12% | |========= | 12% | |========= | 13% | |========== | 14% | |========== | 15% | |=========== | 15% | |=========== | 16% | |============ | 17% | |============ | 18% | |============= | 19% | |============== | 20% | |============== | 21% | |=============== | 22% | |================ | 23% | |================ | 24% | |================= | 24% | |================== | 25% | |================== | 26% | |=================== | 26% | |=================== | 27% | |==================== | 28% | |==================== | 29% | |===================== | 30% | |====================== | 31% | |====================== | 32% | |======================= | 32% | |======================= | 33% | |======================== | 34% | |======================== | 35% | |========================= | 35% | |========================= | 36% | |========================== | 37% | |========================== | 38% | |=========================== | 38% | |=========================== | 39% | |============================ | 40% | |============================= | 41% | |============================= | 42% | |============================== | 43% | |=============================== | 44% | |=============================== | 45% | |================================ | 46% | |================================= | 47% | |================================== | 48% | |================================== | 49% | |=================================== | 49% | |=================================== | 50% | |==================================== | 51% | |==================================== | 52% | |===================================== | 52% | |===================================== | 53% | |====================================== | 54% | |======================================= | 55% | |======================================= | 56% | |======================================== | 57% | |========================================= | 58% | |========================================= | 59% | |========================================== | 60% | |=========================================== | 61% | |=========================================== | 62% | |============================================ | 63% | |============================================= | 64% | |============================================= | 65% | |============================================== | 66% | |=============================================== | 67% | |=============================================== | 68% | |================================================ | 68% | |================================================ | 69% | |================================================= | 70% | |================================================= | 71% | |================================================== | 71% | |=================================================== | 72% | |=================================================== | 73% | |==================================================== | 74% | |===================================================== | 75% | |===================================================== | 76% | |====================================================== | 77% | |======================================================= | 78% | |======================================================= | 79% | |======================================================== | 80% | |========================================================= | 81% | |========================================================= | 82% | |========================================================== | 82% | |========================================================== | 83% | |=========================================================== | 84% | |=========================================================== | 85% | |============================================================ | 85% | |============================================================ | 86% | |============================================================= | 87% | |============================================================= | 88% | |============================================================== | 88% | |============================================================== | 89% | |=============================================================== | 90% | |=============================================================== | 91% | |================================================================ | 91% | |================================================================ | 92% | |================================================================= | 93% | |================================================================= | 94% | |================================================================== | 94% | |=================================================================== | 95% | |=================================================================== | 96% | |==================================================================== | 97% | |===================================================================== | 98% | |===================================================================== | 99% | |======================================================================| 99% | |======================================================================| 100%
# Race/ethnicity parts for minority share
vars_race <- c(tot = "B03002_001", white_nh = "B03002_003")
census_race <- tidycensus::get_acs(
geography = "tract",
variables = vars_race,
year = 2022,
state = "GA",
county = c("Fulton","DeKalb","Clayton"),
geometry = FALSE,
output = "wide"
) |>
dplyr::transmute(
GEOID,
pct_minority = dplyr::if_else(totE > 0, 1 - white_nhE / totE, NA_real_)
) |>
# validate to against noise
dplyr::mutate(pct_minority = pmin(pmax(pct_minority, 0), 1))
## Getting data from the 2018-2022 5-year ACS
# Join to produce the requested `census` object
census <- dplyr::left_join(census_income, census_race, by = "GEOID")
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# Convert the CRS of `census` to GCS
census <- census |>
sf::st_make_valid() |>
sf::st_transform(4326)
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# get centroids of `census` polygons,
# extract centroids that fall inside the `bbox`,
# and assign it to `home` object.
centroids <- census |>
dplyr::mutate(home_pt = sf::st_point_on_surface(geometry)) |>
sf::st_as_sf() |>
sf::st_set_geometry("home_pt")
## Warning: There was 1 warning in `stopifnot()`.
## ℹ In argument: `home_pt = sf::st_point_on_surface(geometry)`.
## Caused by warning in `st_point_on_surface.sfc()`:
## ! st_point_on_surface may not give correct results for longitude/latitude data
home <- centroids[sf::st_within(centroids, bbox, sparse = FALSE)[, 1], ]
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# 1. Get OSM road data for all road types for the `bbox` area.
# 3. Convert the OSM data into an sf object
# 4. Convert osmdata polygons into lines
library(sfnetworks)
library(tidygraph)
q <- osmdata::opq(bbox = sf::st_bbox(bbox)) |>
osmdata::add_osm_feature(key = "highway") # all road types
osm_road <- osmdata::osmdata_sf(q)
# If polygons exist, convert to (multi)lines and bind with lines
if (!is.null(osm_road$osm_polygons) && nrow(osm_road$osm_polygons) > 0) {
poly_lines <- osm_road$osm_polygons |>
sf::st_make_valid() |>
sf::st_cast("MULTILINESTRING") |>
sf::st_cast("LINESTRING")
# append to the existing line set
osm_road$osm_lines <- dplyr::bind_rows(
osm_road$osm_lines |> dplyr::select(dplyr::any_of(c("osm_id","name","highway","oneway","geometry"))),
poly_lines |> dplyr::select(dplyr::any_of(c("osm_id","name","highway","oneway","geometry")))
)
}
## Warning in st_cast.sf(sf::st_cast(sf::st_make_valid(osm_road$osm_polygons), :
## repeating attributes for all sub-geometries for which they may not be constant
# //TASK //////////////////////////////////////////////////////////////////////
osm <- osm_road$osm_lines %>%
sf::st_make_valid() %>%
sf::st_cast("LINESTRING") %>%
dplyr::select(dplyr::any_of(c("osm_id","name","highway","oneway","geometry"))) %>%
sfnetworks::as_sfnetwork(directed = FALSE) %>%
tidygraph::activate("edges")
## Warning in st_cast.sf(., "LINESTRING"): repeating attributes for all
## sub-geometries for which they may not be constant
# Compute weak connected components on nodes, then keep the largest one
# Tag each node with a component id
osm <- osm %>%
tidygraph::activate("nodes") %>%
dplyr::mutate(comp = tidygraph::group_components())
# Find id of the largest component
main_id <- osm %>%
tidygraph::activate("nodes") %>%
dplyr::as_tibble() %>%
dplyr::pull(comp) %>% # comp is a grouping factor
as.integer() %>%
{ which.max(tabulate(.)) }
# Morph to subgraph for the largest component and return it
osm <- osm %>%
tidygraph::morph(tidygraph::to_subgraph, comp == main_id) %>%
tidygraph::unmorph()
## Subsetting by nodes
# TASK ////////////////////////////////////////////////////////////////////////
# Add a new column named 'length' to the edges part of the object `osm`.
osm <- osm %>%
tidygraph::activate("edges") %>%
dplyr::mutate(length = units::drop_units(sf::st_length(geometry)))
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Extract the first row from `home` object and store it `home_1`
home_1 <- home[1,]
# =========== NO MODIFY ZONE ENDS HERE ========================================
# TASK ////////////////////////////////////////////////////////////////////////
# Find the shortest paths from `home_1` to all other stations
paths <- sfnetworks::st_network_paths(
x = osm,
from = sf::st_geometry(home_1),
to = sf::st_geometry(station),
weights = "length", # use edge length (meters)
type = "shortest"
)
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Get shortest network distances from `home_1` to all other stations.
dist_all <- map_dbl(1:nrow(paths), function(x){
osm %>%
activate("nodes") %>%
slice(paths$node_paths[[x]]) %>%
st_as_sf("edges") %>%
pull(length) %>%
sum()
}) %>% unlist()
# Replace zeros with a large value.
if (any(dist_all == 0)){
dist_all[dist_all == 0] <- max(dist_all)
}
# =========== NO MODIFY ZONE ENDS HERE =======================================
# TASK ////////////////////////////////////////////////////////////////////////
# Find the closest station and assign the value to `closest_station`
closest_station <- station[which.min(dist_all), ]
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# Get the distance to the closest station.
closest_dist <- min(dist_all, na.rm = TRUE)
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# Calculate travel time based on the `closest_dist` assuming we drive at 20 miles/hour speed.
# Assign the value to `trvt_osm_m` object.
trvt_osm_m <- (closest_dist / 1609.344) / 20 * 60
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# Create a subset of stop_times for date = 2025-11-10, 7–10AM.
# --- Option class helper (filter_stop_times) ---
if (exists("filter_stop_times")) {
am_stop_time <- filter_stop_times(
gtfs_obj = gtfs,
extract_date = "2025-11-10",
min_departure_time = 3600 * 7,
max_arrival_time = 3600 * 10
)
} else {
# --- Using gtfsrouter ---
# Ensure a transfers table exists.
# Build a timetable for that service day and then keep 7–10AM.
tt <- gtfsrouter::gtfs_timetable(gtfs, day = "2025-11-10", quiet = TRUE)
am_stop_time <- tt$d %>%
dplyr::filter(departure_time >= "07:00:00", arrival_time <= "10:00:00")
}
# //TASK //////////////////////////////////////////////////////////////////////
# TASK ////////////////////////////////////////////////////////////////////////
# 1) Travel time from closest station to all stations allowing ONE transfer,
# 2) Keep only trips that end at Midtown (stop_id == "134"),
# 3) Assign to `trvt`.
if (exists("travel_times")) {
trvt <- travel_times(
filtered_stop_times = am_stop_time,
stop_name = closest_station$stop_name,
max_transfers = 1,
return_coords = FALSE
) %>%
dplyr::filter(to_stop_id == midtown$stop_id[1])
} else {
# Use stop_id (more reliable than name)
res <- gtfsrouter::gtfs_traveltimes(
gtfs = gtfs,
from = closest_station$stop_id[1],
start_time = "07:00:00",
day = "2025-11-10",
route_pattern = NULL,
quiet = TRUE
)
trvt <- res %>%
dplyr::filter(to_stop_id == midtown$stop_id[1]) %>%
dplyr::slice_min(.data$travel_time, n = 1, with_groups = FALSE)
}
# //TASK //////////////////////////////////////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
# Divide the calculated travel time by 60 to convert the unit from seconds to minutes.
trvt_gtfs_m <- trvt$travel_time/60
# Add the travel time from home to the nearest station and
# the travel time from the nearest station to Midtown station
total_trvt <- trvt_osm_m + trvt_gtfs_m
# =========== NO MODIFY ZONE ENDS HERE ========================================
# Function definition (do not modify other parts of the code in this code chunk except for those inside the TASK section)
get_trvt <- function(home, osm, station, midtown){
# TASK ////////////////////////////////////////
# Begin Step 4 logic packed into a function
# choose the single origin (first row if multiple given)
home_1 <- home[1,]
# shortest paths on the road network from home to all stations
paths <- sfnetworks::st_network_paths(
x = osm,
from = sf::st_geometry(home_1),
to = sf::st_geometry(station),
weights = "length",
type = "shortest"
)
# sum edge lengths for each path (meters)
dist_all <- purrr::map_dbl(paths$edge_paths, function(idx_edges){
if (length(idx_edges) == 0) return(Inf)
osm %>%
tidygraph::activate("edges") %>%
dplyr::slice(idx_edges) %>%
sf::st_as_sf() %>%
dplyr::pull(length) %>%
sum(na.rm = TRUE)
}) %>% unlist()
if (any(dist_all == 0)) {
dist_all[dist_all == 0] <- max(dist_all, na.rm = TRUE)
}
# nearest station & distance
closest_station <- station[which.min(dist_all), ]
closest_dist <- min(dist_all, na.rm = TRUE)
# driving minutes at 20 mph (meters -> miles -> hours -> minutes)
trvt_osm_m <- (closest_dist / 1609.344) / 20 * 60
# build a 7–10am timetable for a specific day, then compute rail time
# Uses class helper if available; otherwise falls back to gtfsrouter.
if (exists("filter_stop_times")) {
am_stop_time <- filter_stop_times(
gtfs_obj = gtfs,
extract_date = "2025-11-10",
min_departure_time = 3600 * 7,
max_arrival_time = 3600 * 10
)
} else {
tt <- gtfsrouter::gtfs_timetable(gtfs, day = "2025-11-10", quiet = TRUE)
am_stop_time <- tt$d %>%
dplyr::filter(departure_time >= "07:00:00", arrival_time <= "10:00:00")
}
if (exists("travel_times")) {
trvt <- travel_times(
filtered_stop_times = am_stop_time,
stop_name = closest_station$stop_name,
max_transfers = 1,
return_coords = FALSE
) %>%
dplyr::filter(to_stop_id == midtown$stop_id[1])
} else {
res <- gtfsrouter::gtfs_traveltimes(
gtfs = gtfs,
from = closest_station$stop_id[1],
start_time = "07:00:00",
day = "2025-11-10",
quiet = TRUE
)
trvt <- res %>%
dplyr::filter(to_stop_id == midtown$stop_id[1]) %>%
dplyr::slice_min(.data$travel_time, n = 1, with_groups = FALSE)
}
trvt_gtfs_m <- trvt$travel_time / 60
total_trvt <- trvt_osm_m + trvt_gtfs_m
# End Step 4 logic
# //TASK //////////////////////////////////////
# =========== NO MODIFICATION ZONE STARTS HERE ===============================
if (length(total_trvt) == 0) {total_trvt = 0}
return(total_trvt)
# =========== NO MODIFY ZONE ENDS HERE ========================================
}
# Prepare an empty vector
total_trvt <- vector("numeric", nrow(home))
# Apply the function to all Census Tracts
# Fill `total_trvt` object with the calculated time
for (i in 1:nrow(home)){
total_trvt[i] <- get_trvt(home[i,], osm, station, midtown)
}
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
# combine the calculated travel time to `home`
home <- home %>%
cbind(trvt = total_trvt)
#BUG REMOVER BLOCK FOR STEP 7
home_vis <- dplyr::mutate(home, travel_min = total_trvt)
# safety: make sure CRS of home and osm match
home <- sf::st_transform(home, sf::st_crs(osm))
# a safe wrapper so a single failure doesn’t stop the run
get_trvt_safely <- purrr::safely(function(i) {
get_trvt(home = home[i, ], osm = osm, station = station, midtown = midtown)
})
# compute total travel time (minutes) for each centroid
res <- purrr::map( seq_len(nrow(home)), get_trvt_safely )
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
## Warning in shortest_paths(x, from, to, weights = weights, output = "both", : At
## vendor/cigraph/src/paths/dijkstra.c:534 : Couldn't reach some vertices.
# extract results (NULL -> NA_real_)
home$total_trvt <- vapply(res, function(x) if (is.null(x$result)) NA_real_ else as.numeric(x$result), numeric(1))
home_vis <- dplyr::mutate(home, travel_min = total_trvt)
# join attributes for scatterplots
plot_df <- home_vis |>
sf::st_drop_geometry() |>
dplyr::select(GEOID, travel_min) |>
dplyr::left_join(
census |> sf::st_drop_geometry() |> dplyr::select(GEOID, hhinc, pct_minority),
by = "GEOID"
) |>
dplyr::filter(!is.na(travel_min), !is.na(hhinc), !is.na(pct_minority))
# Ensure POINT geometry for home
home_pts <- home
if (!inherits(sf::st_geometry(home_pts), "sfc_POINT")) {
home_pts <- sf::st_point_on_surface(home_pts) # safer than centroid for oddly-shaped tracts
}
## Warning: st_point_on_surface assumes attributes are constant over geometries
## Warning in st_point_on_surface.sfc(st_geometry(x)): st_point_on_surface may not
## give correct results for longitude/latitude data
home_pts <- sf::st_transform(home_pts, 4326)
# carry travel time column forward (rename for clarity)
home_pts <- dplyr::mutate(home_pts, travel_min = total_trvt)
pal_time <- colorBin("YlOrRd", domain = home_pts$travel_min, bins = 7, na.color = "#666666")
leaflet(options = leafletOptions(preferCanvas = TRUE)) |>
addProviderTiles("CartoDB.Positron") |>
addPolygons(
data = census,
fillColor = ~colorBin("Blues", census$hhinc, 7)(hhinc),
fillOpacity = 0.75, weight = 0.5, color = "#444"
) |>
addCircleMarkers(
data = home_pts,
radius = 5, stroke = TRUE, weight = 1, color = "#333",
fillColor = ~pal_time(travel_min), fillOpacity = 0.95,
label = ~sprintf("Travel time: %.1f min", travel_min)
)
## Warning in pal_time(travel_min): Some values were outside the color scale and
## will be treated as NA
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette YlOrRd is 9
## Returning the palette you asked for with that many colors
library(sf)
library(dplyr)
library(leaflet)
library(htmltools)
library(scales)
##
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
##
## discard
library(ggplot2)
#Ensure travel_min exists (minutes)
if (!"total_trvt" %in% names(home)) home$total_trvt <- NA_real_
home_vis <- dplyr::mutate(home, travel_min = as.numeric(total_trvt))
#Build POINT layer from home_vis (handle polygons)
if (!inherits(sf::st_geometry(home_vis), "sfc_POINT")) {
home_pts <- home_vis %>%
dplyr::mutate(.pt = sf::st_point_on_surface(sf::st_geometry(home_vis))) %>%
sf::st_as_sf() %>%
sf::st_set_geometry(".pt")
} else {
home_pts <- home_vis
}
## Warning: There was 1 warning in `stopifnot()`.
## ℹ In argument: `.pt = sf::st_point_on_surface(sf::st_geometry(home_vis))`.
## Caused by warning in `st_point_on_surface.sfc()`:
## ! st_point_on_surface may not give correct results for longitude/latitude data
home_pts <- sf::st_transform(home_pts, 4326)
tt <- suppressWarnings(as.numeric(home_pts$travel_min))
keep <- is.finite(tt)
home_pts <- home_pts[keep, ]
tt <- tt[keep]
inc_dom <- census$hhinc[is.finite(census$hhinc)]
min_dom <- census$pct_minority[is.finite(census$pct_minority)]
brks_tt <- pretty(tt, n = 8); if (length(unique(tt)) == 1) brks_tt <- c(tt[1]-1, tt[1], tt[1]+1)
pal_trvt <- leaflet::colorBin("YlOrRd", domain = tt, bins = brks_tt, na.color = "transparent")
pal_inc <- leaflet::colorBin("Blues", domain = inc_dom, bins = 7, na.color = "#cccccc")
pal_min <- leaflet::colorBin("Purples",domain = min_dom, bins = 7, na.color = "#cccccc")
# TASK ////////////////////////////////////////
# Create an interactive map displaying census (polygons) and home (points), effectively visualizing household income and travel time to Midtown Station, respectively.
leaflet(options = leafletOptions(preferCanvas = TRUE, minZoom = 9)) |>
addProviderTiles(providers$CartoDB.Positron) |>
addPolygons(
data = census,
fillColor = ~pal_inc(hhinc), fillOpacity = 0.8,
color = "#4a4a4a", weight = 0.6, opacity = 0.9,
label = ~lapply(sprintf("%s<br>Median HH income: %s", NAME, dollar(hhinc)), HTML),
highlight = highlightOptions(weight = 2, color = "#000", bringToFront = TRUE)
) |>
addCircleMarkers(
data = home_pts,
radius = 5, stroke = TRUE, weight = 1, color = "grey20",
fillColor = ~pal_trvt(travel_min), fillOpacity = 0.95,
label = ~sprintf("Travel time to Midtown: %.1f min", travel_min)
) |>
addLegend("bottomleft", pal = pal_inc, values = inc_dom, title = "Median HH Income") |>
addLegend("bottomright", pal = pal_trvt, values = tt, title = "Travel Time (min)")
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette YlOrRd is 9
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette YlOrRd is 9
## Returning the palette you asked for with that many colors
# Create an interactive map displaying census (polygons) and home (points) effectively visualizing the percentage of minority population and travel time to Midtown Station, respectively.
leaflet(options = leafletOptions(preferCanvas = TRUE, minZoom = 9)) |>
addProviderTiles(providers$CartoDB.Positron) |>
addPolygons(
data = census,
fillColor = ~pal_min(pct_minority), fillOpacity = 0.8,
color = "#4a4a4a", weight = 0.6, opacity = 0.9,
label = ~lapply(sprintf("%s<br>Minority share: %s", NAME, percent(pct_minority, 0.1)), HTML),
highlight = highlightOptions(weight = 2, color = "#000", bringToFront = TRUE)
) |>
addCircleMarkers(
data = home_pts,
radius = 5, stroke = TRUE, weight = 1, color = "grey20",
fillColor = ~pal_trvt(travel_min), fillOpacity = 0.95,
label = ~sprintf("Travel time to Midtown: %.1f min", travel_min)
) |>
addLegend("bottomleft", pal = pal_min, values = min_dom, title = "Minority Share") |>
addLegend("bottomright", pal = pal_trvt, values = tt, title = "Travel Time (min)")
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette YlOrRd is 9
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(max(3, n), palette): n too large, allowed maximum for palette YlOrRd is 9
## Returning the palette you asked for with that many colors
# Create a scatter plot with a trend line showing the relationship between household income (x-axis) and travel time to Midtown Station (y-axis).
plot_df <- home_pts |>
sf::st_drop_geometry() |>
dplyr::select(GEOID, travel_min) |>
dplyr::left_join(
census |> sf::st_drop_geometry() |> dplyr::select(GEOID, hhinc),
by = "GEOID"
) |>
dplyr::filter(is.finite(travel_min), is.finite(hhinc))
ggplot(plot_df, aes(x = hhinc, y = travel_min)) +
geom_point(alpha = 0.6, size = 1.8) +
geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +
scale_x_continuous(labels = dollar_format()) +
labs(x = "Median household income ($)", y = "Travel time to Midtown (min)",
title = "Income vs. Travel Time (Park-and-Ride)") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# Create a scatter plot with a trend line showing the relationship between the percentage of minority population (x-axis) and travel time to Midtown Station (y-axis).
plot_df2 <- home_pts |>
sf::st_drop_geometry() |>
dplyr::select(GEOID, travel_min) |>
dplyr::left_join(
census |> sf::st_drop_geometry() |> dplyr::select(GEOID, pct_minority),
by = "GEOID"
) |>
dplyr::filter(is.finite(travel_min), is.finite(pct_minority))
ggplot(plot_df2, aes(x = pct_minority, y = travel_min)) +
geom_point(alpha = 0.6, size = 1.8) +
geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +
scale_x_continuous(labels = percent_format(accuracy = 1)) +
labs(x = "Minority population share", y = "Travel time to Midtown (min)",
title = "Minority Share vs. Travel Time") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# //TASK //////////////////////////////////////
##Summary of Findings
The visualizations reveal interesting patterns in how accessibility to Midtown Atlanta via park-and-ride varies across socioeconomic and demographic contexts.
The income vs. travel time plot shows a slightly positive trend, meaning tracts with higher median household income tend to have longer travel times to Midtown. This suggests that wealthier residents are more likely to live farther from the urban core—perhaps in suburban areas with larger lots or less direct MARTA connectivity. Conversely, tracts with lower household income often experience shorter travel times, reflecting their proximity to central transit corridors and MARTA stations.
The minority share vs. travel time plot shows a slightly negative relationship: tracts with higher proportions of minority residents tend to have shorter travel times to Midtown. This is consistent with Atlanta’s historical development patterns, where many minority communities are concentrated in neighborhoods closer to downtown and major rail lines.
Taken together, the maps and scatter plots indicate that proximity to transit access points—and consequently, shorter commutes—is not necessarily an indicator of privilege. In Atlanta, higher-income areas may trade accessibility for space, while lower-income and minority communities are more centrally located, benefiting from faster access to core employment centers like Midtown.