library(dplyr) # for data manipulation
library(tidyr) # for tidy data operations (pivot functions)
library(readr) # read and write tabular data
library(stringr) # manipulate strings
library(forcats) # process categorical data
library(fs) # for file system management
library(sf) # for simple feature geometries
library(arcgislayers) # to read arcgis rest services
library(janitor) # clean column names
library(units) # set and adjust units of measurement
library(tmap) # view sf objects1 Introduction
This analysis examines the distribution of roadway and bikeway infrastructure across Utah municipalities. The primary objectives are to:
- Calculate total road lane miles for each municipality
- Calculate bikeway lane miles by facility type for each municipality
- Compare roadway and bikeway infrastructure across municipalities
All spatial data is sourced from the Utah Geospatial Resource Center (UGRC) and processed using the R spatial ecosystem.
2 Setup Environment
2.1 Install Packages
The following packages are required for this analysis. Install them if not already available in your R environment.
install.packages("dplyr")
install.packages("tidyr")
install.packages("readr")
install.packages("stringr")
install.packages("forcats")
install.packages("fs")
install.packages("sf")
install.packages("arcgislayers")
install.packages("janitor")
install.packages("units")
install.packages("tmap")2.2 Load Packages
2.3 Environment Variables
We use EPSG:3566 (NAD83(HARN) / Utah North) projection, which uses US survey feet as the base unit. This projection is appropriate for statewide analysis in Utah and ensures accurate distance calculations.
# Set Project CRS
PROJECT_CRS = "EPSG:3566"
# Set tmap mode to interactive
tmap::tmap_mode("view")3 Read Data from UGRC
All spatial datasets are downloaded from UGRC’s ArcGIS REST services. To improve performance and enable offline work, the data is cached locally as GeoDatabase files. Each file is downloaded the first time the post is rendered and reused afterwards, so nothing needs to be fetched by hand (the statewide roads file is large and is not committed).
# set data folder
dir_data <- "_data"
fs::dir_create(dir_data) # Create a directory if it doesnt exist
# UGRC downloads are paged (the roads layer is over 200 pages), so one dropped
# connection fails the whole read. Retry a few times before giving up.
arc_read_retry <- function(url, ..., tries = 5) {
for (i in seq_len(tries)) {
out <- tryCatch(arcgislayers::arc_read(url, ...), error = function(e) e)
if (!inherits(out, "error")) return(out)
if (i == tries) stop(out)
message("Download failed (attempt ", i, " of ", tries, "), retrying...")
Sys.sleep(5 * i)
}
}3.1 Utah City Boundaries
Municipal boundaries are used to aggregate roadway and bikeway data by city. These boundaries represent the legal limits of incorporated municipalities in Utah.
# Define paths
path_ut_cities <- file.path(dir_data, "UtahMunicipalBoundaries.gpkg")
# Download if not exists
if (!fs::file_exists(path_ut_cities)) {
# Read data using arcgis package
sf_ut_cities <- arc_read_retry(
"https://services1.arcgis.com/99lidPhWCzftIe9K/ArcGIS/rest/services/UtahMunicipalBoundaries/FeatureServer/0",
crs = PROJECT_CRS
) |> janitor::clean_names()
# Write to a GeoDatabase
sf_ut_cities |>
sf::st_write(path_ut_cities, layer = "UtahMunicipalBoundaries", append = FALSE)
}
# Always read from the local copy, so the geometry column is the same
# whether the file was just downloaded or already existed
sf_ut_cities <- sf::st_read(path_ut_cities, quiet = TRUE) |>
sf::st_transform(PROJECT_CRS)tmap::qtm(sf_ut_cities)3.2 Utah County Boundaries
County boundaries are used to aggregate roadway and bikeway data for unincorporated areas in Utah.
# Define paths
path_ut_counties <- file.path(dir_data, "UtahCountyBoundaries.gpkg")
# Download if not exists
if (!fs::file_exists(path_ut_counties)) {
# Read data using arcgis package
sf_ut_counties <- arc_read_retry(
"https://services1.arcgis.com/99lidPhWCzftIe9K/ArcGIS/rest/services/UtahCountyBoundaries/FeatureServer/0",
crs = PROJECT_CRS
) |> janitor::clean_names()
# Write to a GeoDatabase
sf_ut_counties |>
sf::st_write(path_ut_counties, layer = "UtahCountyBoundaries", append = FALSE)
}
# Always read from the local copy, so the geometry column is the same
# whether the file was just downloaded or already existed
sf_ut_counties <- sf::st_read(path_ut_counties, quiet = TRUE) |>
sf::st_transform(PROJECT_CRS)tmap::qtm(sf_ut_cities)3.3 Utah Roadways
The Utah Roads dataset contains all roadway centerlines statewide, including the number of through lanes for each road segment. This forms the basis for calculating road lane miles.
# Define paths
path_ut_roads <- file.path(dir_data, "UtahRoads.gpkg")
# Download if not exists
if (!fs::file_exists(path_ut_roads)) {
# Read data using arcgis package
sf_ut_roads <- arc_read_retry(
"https://services1.arcgis.com/99lidPhWCzftIe9K/ArcGIS/rest/services/UtahRoads/FeatureServer/0",
crs = PROJECT_CRS
) |> janitor::clean_names()
# Write to a GeoDatabase
sf_ut_roads |>
sf::st_write(path_ut_roads, layer = "UtahRoads", append = FALSE)
}
# Always read from the local copy, so the geometry column is the same
# whether the file was just downloaded or already existed
sf_ut_roads <- sf::st_read(path_ut_roads, quiet = TRUE) |>
sf::st_transform(PROJECT_CRS)tmap::qtm(sf_ut_roadways)3.4 Utah Bikeways
The Bikeways dataset contains all existing bicycle facilities in Utah. Each segment has two facility type attributes (facility1 and facility2) representing the bicycle facility in each direction of travel.
# Define paths
path_ut_bikeways <- file.path(dir_data, "Bikeways.gpkg")
# Download if not exists
if (!fs::file_exists(path_ut_bikeways)) {
# Read data using arcgis package
sf_ut_bikeways <- arc_read_retry(
"https://services1.arcgis.com/99lidPhWCzftIe9K/ArcGIS/rest/services/Bikeways/FeatureServer/0",
crs = PROJECT_CRS
) |> janitor::clean_names()
# Write to a GeoDatabase
sf_ut_bikeways |>
sf::st_write(path_ut_bikeways, layer = "Bikeways", append = FALSE)
}
# Always read from the local copy, so the geometry column is the same
# whether the file was just downloaded or already existed
sf_ut_bikeways <- sf::st_read(path_ut_bikeways, quiet = TRUE) |>
sf::st_transform(PROJECT_CRS)tmap::qtm(sf_ut_bikeways)