In this lab, we will be using spatial data to run a network analysis. We will be playing around with data from Davis, including geocoding fast food restaurants in our fair city, then mapping the most efficient route to bike between all fast food establishments. Next, we will estimate the travel time between all fast food establishments in Davis. Finally, we are going to create a cool visualization of the travel times from the building that we are sitting in! This is all intended to explain how you can use spatial data to understand how different spatial features relate using the road networks that we travel on.
The objectives of this guide are to teach you to:
Let’s get rollin…
First, we always gotta remember to load our packages.
library(tidygeocoder)
library(sf)
## Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(tmap)
library(osrm)
## Data: (c) OpenStreetMap contributors, ODbL 1.0 - http://www.openstreetmap.org/copyright
## Routing: OSRM - http://project-osrm.org/
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.2 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
R offers a few packages for network analysis. We will be using the osrm package because it is fast and open source. It is an interface between R and the Open Street Map-Based Routing Service. It has a number of options, including:
osrmTrip: Build and send an OSRM API query to get the
shortest travel geometry between multiple unordered points. This
function interfaces the trip OSRM service. Use this function to resolve
the travelling salesman problem.osrmTable: Build and send OSRM API queries to get
travel time matrices between points. This function interfaces the table
OSRM service.osrmIsochrone: This function computes areas that are
reachable within a given time span from a point and returns the
reachable regions as polygons. These areas of equal travel time are
called isochrones.osrmRoute: Build and send an OSRM API query to get the
travel geometry between two points. This function interfaces with the
route OSRM service.osrmNearest: Build and send an OSRM API query to get
the nearest point on the street network. This function interfaces the
nearest OSRM service.osrmIsodistance: This function computes areas that are
reachable within a given road distance from a point and returns the
reachable regions as polygons. These areas of equal travel distance are
called isodistances.
Note that there are other options for network analyses in R, including gmapsdistance, which uses the Google Maps API, stplanr, and others. But many of these require an API key and may even cost money if you use them too much! So for now, we will stick to osrm.
Similar to what we did in one of our first labs, let’s bring in the
addresses of all fast food establishments in Davis and geocode them! We
will first read in the .csv from the website using read_csv
from the tidyverse package.
download.file("https://raw.githubusercontent.com/pjames-ucdavis/SPH215/refs/heads/main/fast_food_davis_ca.csv", destfile = "fast_food_davis_ca.csv", mode = "wb")
ff <- read_csv("fast_food_davis_ca.csv")
## Rows: 25 Columns: 2
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): Restaurant Name, Address
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(ff)
## Rows: 25
## Columns: 2
## $ `Restaurant Name` <chr> "In-N-Out Burger", "Dairy Queen", "Carl's Jr.", "Chi…
## $ Address <chr> "1020 Olive Dr, Davis, CA 95616", "909 5th St, Davis…
OK, so looks like Address is the variable we want to focus
on. Let’s geocode the Davis fast food data using the the
geocode() function in tidygeocoder. And
let’s specify that we want to use the “arcgis” geocoder.
ff_geo <- geocode(ff, address = "Address",
method = "arcgis")
## Passing 25 addresses to the ArcGIS single address geocoder
## Query completed in: 9.7 seconds
Alrighty that ran! Now let’s check our data and make sure there’s no missingness in our lat and long fields.
summary(ff_geo$lat)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 38.54 38.54 38.55 38.55 38.55 38.56
summary(ff_geo$long)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -121.8 -121.7 -121.7 -121.7 -121.7 -121.7
Looks like there’s no missingness! No NAs! OK, now we use
st_as_sf from the sf package to make the
dataset spatial and we use the CRS of 4326 because we have longitude and
latitude.
ff_pts <- st_as_sf(ff_geo, coords=c("long","lat"), crs=4326)
Now let’s use the osrm package to calculate an optimal trip between all fast food establishments in Davis, then let’s take a look at the output. This means we are estimating the shortest travel geometry between all fast food establishments in Davis. And we are in Davis, so of course we will estimate this on bike! Maybe useful for an SPH 215 pub crawl in the future?
trip <- osrmTrip(ff_pts, osrm.profile = "bike")
glimpse(trip)
## List of 1
## $ :List of 2
## ..$ trip :Classes 'sf' and 'data.frame': 25 obs. of 5 variables:
## .. ..$ start : chr [1:25] "1" "7" "9" "10" ...
## .. ..$ end : chr [1:25] "7" "9" "10" "18" ...
## .. ..$ duration: num [1:25] 5.52 1.16 4.51 0.1 12.72 ...
## .. ..$ distance: num [1:25] 1.05 0.155 0.859 0.01 3.025 ...
## .. ..$ geometry:sfc_LINESTRING of length 25; first list element: 'XY' num [1:10, 1:2] -122 -122 -122 -122 -122 ...
## .. ..- attr(*, "sf_column")= chr "geometry"
## .. ..- attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA
## .. .. ..- attr(*, "names")= chr [1:4] "start" "end" "duration" "distance"
## ..$ summary:List of 2
## .. ..$ duration: num 98.8
## .. ..$ distance: num 21
OK, it ran! Some good info in there. Looks like we have a
List with an sf dataset, a data.frame, and a list of duration
and distance. Now, let’s visualize what this path might look like using
the tmap package. We will pull out the route as an
sf object and store it as trip_sf. Then we
will map that and specify that we want the lines to be the route in
blue, and then we want to see the location of our fast food
establishments ff_pts in red bubbles. We also can label the
restaurants using tm_text and set an offset with
xmod and ymod options.
# Extract the linestring route (as an sf object)
trip_sf <- trip[[1]]$trip
# Set tmap to interactive mode
tmap_mode("view")
## ℹ tmap modes "plot" - "view"
## ℹ toggle with `tmap::ttm()`
# Plot with tmap
tm <- tm_shape(trip_sf) +
tm_lines(lwd = 3, col = "blue") +
tm_shape(ff_pts) +
tm_bubbles(size = 0.5, col = "red") +
tm_text("Restaurant Name", size=1, xmod = 0.25, ymod=0.25)
tm
Spectacular! Let’s now get some summary stats for each leg of our
fast food extravaganza. We can use the mutate() function
from tidyverse to get this info.
leg_summary <- trip_sf %>%
mutate(duration = round(duration, 1),
distance = round(distance, 1))
leg_summary
## Simple feature collection with 25 features and 4 fields
## Geometry type: LINESTRING
## Dimension: XY
## Bounding box: xmin: -121.7704 ymin: 38.53981 xmax: -121.6934 ymax: 38.56251
## Geodetic CRS: WGS 84
## First 10 features:
## start end duration distance geometry
## 1 1 7 5.5 1.1 LINESTRING (-121.7371 38.54...
## 2 7 9 1.2 0.2 LINESTRING (-121.7326 38.54...
## 3 9 10 4.5 0.9 LINESTRING (-121.7319 38.54...
## 4 10 18 0.1 0.0 LINESTRING (-121.7239 38.54...
## 5 18 14 12.7 3.0 LINESTRING (-121.7238 38.54...
## 6 14 5 2.2 0.4 LINESTRING (-121.6974 38.55...
## 7 5 3 19.8 4.7 LINESTRING (-121.6934 38.55...
## 8 3 25 12.8 3.1 LINESTRING (-121.7317 38.56...
## 9 25 12 0.7 0.1 LINESTRING (-121.7651 38.56...
## 10 12 13 3.3 0.7 LINESTRING (-121.7653 38.56...
OK, we we have the duration and distance for each trip. Let’s get the summary of the whole trip in minutes and kilometers.
trip_summary <- trip[[1]]$summary
trip_summary
## $duration
## [1] 98.76
##
## $distance
## [1] 21.0118
So it was nice to get an optimal route, but what if I wanted the travel times between all of the fast food establishments in Davis? You could imagine doing this with addresses of geocoded participants and fast food establishments to create a dataset with distance to the closest fast food establishment.
ff_table <- osrmTable(ff_pts,
osrm.profile = "bike")
glimpse(ff_table)
## List of 3
## $ durations : num [1:25, 1:25] 0 6.7 13.5 4 19.5 5.2 3.7 4.7 3.9 7 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:25] "1" "2" "3" "4" ...
## .. ..$ : chr [1:25] "1" "2" "3" "4" ...
## $ sources :'data.frame': 25 obs. of 2 variables:
## ..$ lon: num [1:25] -122 -122 -122 -122 -122 ...
## ..$ lat: num [1:25] 38.5 38.5 38.6 38.5 38.6 ...
## $ destinations:'data.frame': 25 obs. of 2 variables:
## ..$ lon: num [1:25] -122 -122 -122 -122 -122 ...
## ..$ lat: num [1:25] 38.5 38.5 38.6 38.5 38.6 ...
Great. Now let’s look at the duration matrix.
ff_table$duration
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
## 1 0.0 6.3 13.4 3.9 18.2 5.0 5.5 4.4 5.5 7.6 14.8 18.4 21.1 18.3 3.6
## 2 6.7 0.0 9.3 4.7 20.6 1.8 10.1 2.3 10.1 8.9 12.7 15.9 18.7 19.7 4.2
## 3 13.5 9.4 0.0 13.3 19.8 10.6 14.2 11.0 14.1 11.1 10.0 13.0 15.7 19.3 12.8
## 4 4.0 4.7 13.2 0.0 21.2 3.3 7.4 2.7 7.3 9.4 12.6 16.5 19.0 20.7 1.4
## 5 19.5 20.8 19.8 22.0 0.0 22.0 16.9 21.9 16.5 14.0 29.4 32.3 35.1 2.2 21.8
## 6 5.2 1.8 10.6 3.2 21.8 0.0 8.6 0.8 8.6 10.1 11.9 15.1 17.9 20.9 2.8
## 7 3.7 8.6 14.6 6.2 16.8 7.3 0.0 6.7 1.2 5.0 17.1 20.6 23.4 15.5 6.0
## 8 4.7 2.2 11.0 2.8 21.6 0.9 8.2 0.0 8.1 10.2 12.1 15.3 18.1 21.1 2.3
## 9 3.9 8.8 14.2 6.4 16.5 7.5 1.1 6.9 0.0 4.5 17.3 20.9 23.6 15.2 6.2
## 10 7.0 9.0 10.9 9.5 14.1 10.2 4.8 10.0 4.8 0.0 20.4 23.3 26.1 12.8 9.4
## 11 15.1 12.4 9.7 12.7 28.8 11.5 18.5 11.8 18.5 20.1 0.0 4.2 7.0 28.3 11.8
## 12 18.3 15.5 12.8 16.2 31.9 14.5 21.7 14.8 21.7 23.2 3.9 0.0 3.3 31.4 15.3
## 13 21.4 18.6 15.9 19.3 35.0 17.7 24.8 18.0 24.8 26.3 7.0 3.8 0.0 34.5 18.4
## 14 17.9 19.4 19.1 20.4 2.2 20.6 15.2 20.9 14.8 12.4 28.6 31.5 34.3 0.0 20.2
## 15 3.7 4.2 12.8 1.4 21.0 2.9 7.2 2.3 7.1 9.3 11.7 15.7 18.2 20.5 0.0
## 16 2.7 4.1 12.6 2.4 20.0 2.7 6.2 2.1 6.1 8.2 12.9 16.1 18.9 19.5 2.1
## 17 4.4 4.5 13.0 2.1 21.7 3.2 7.8 2.7 7.8 9.9 11.3 15.2 17.7 21.1 1.2
## 18 7.1 9.1 11.0 9.6 14.0 10.3 4.9 10.1 4.9 0.1 20.5 23.4 26.2 12.7 9.5
## 19 3.2 4.3 12.8 1.4 20.5 3.0 6.7 2.4 6.7 8.8 11.8 15.7 18.2 20.0 0.5
## 20 3.1 4.5 13.0 1.2 20.4 3.1 6.5 2.5 6.5 8.6 12.7 16.7 19.2 19.8 1.5
## 21 4.9 1.8 10.5 2.9 21.7 0.4 8.3 0.4 8.2 10.1 11.8 15.1 17.9 20.9 2.4
## 22 6.8 2.8 9.3 4.8 23.0 1.9 10.2 2.4 10.2 11.3 10.5 13.7 16.5 22.0 4.3
## 23 4.4 2.9 11.4 2.4 21.6 1.6 7.8 0.8 7.7 9.8 12.2 15.4 18.1 21.1 1.9
## 24 3.1 5.0 13.4 2.1 20.4 3.7 6.6 3.0 6.6 8.7 12.5 16.4 18.9 19.9 1.2
## 25 18.6 15.8 13.1 16.5 32.2 14.9 22.0 15.2 22.0 23.5 4.3 0.7 4.0 31.7 15.6
## 16 17 18 19 20 21 22 23 24 25
## 1 2.3 4.2 7.7 3.1 3.0 4.5 6.5 3.6 2.9 18.2
## 2 4.1 4.6 8.9 4.4 4.6 1.9 2.8 3.2 5.1 15.7
## 3 12.6 13.1 11.2 12.9 12.9 10.7 9.5 11.7 13.6 12.8
## 4 2.4 2.0 9.5 1.5 1.2 2.9 4.8 2.1 2.1 16.3
## 5 20.4 22.4 14.0 21.3 21.1 21.9 23.1 21.7 21.1 32.1
## 6 2.7 3.1 10.2 2.9 3.1 0.4 2.0 1.7 3.7 14.9
## 7 4.5 6.5 5.1 5.5 5.3 6.8 8.7 5.9 5.3 20.5
## 8 2.2 2.6 10.3 2.4 2.5 0.4 2.4 1.1 3.1 15.1
## 9 4.8 6.8 4.6 5.7 5.5 7.0 8.9 6.1 5.5 20.7
## 10 7.9 9.9 0.1 8.8 8.6 10.2 11.3 9.3 8.7 23.1
## 11 12.5 11.5 20.2 12.0 12.9 11.5 10.1 11.6 12.7 4.0
## 12 15.6 15.0 23.3 15.4 16.0 14.6 13.2 14.7 16.2 1.2
## 13 18.7 18.1 26.4 18.5 19.1 17.7 16.3 17.8 19.3 4.6
## 14 18.7 20.8 12.4 19.7 19.5 20.7 21.7 20.1 19.5 31.3
## 15 2.0 1.2 9.4 0.5 1.6 2.5 4.4 1.7 1.2 15.5
## 16 0.0 2.6 8.3 1.5 1.3 2.3 4.2 1.4 1.7 15.9
## 17 2.7 0.0 10.0 1.3 2.3 2.8 4.7 2.1 2.1 15.0
## 18 8.0 10.0 0.0 8.9 8.7 10.3 11.4 9.4 8.8 23.2
## 19 1.6 1.2 8.9 0.0 1.1 2.5 4.5 1.8 0.8 15.5
## 20 1.3 2.1 8.7 1.0 0.0 2.7 4.7 1.9 1.4 16.5
## 21 2.3 2.7 10.2 2.5 2.7 0.0 2.0 1.3 3.3 14.9
## 22 4.2 4.7 11.3 4.5 4.6 2.0 0.0 3.2 5.2 13.5
## 23 1.7 2.2 9.9 2.1 2.1 1.1 3.1 0.0 2.8 15.2
## 24 1.8 1.9 8.8 0.7 1.4 3.2 5.2 2.4 0.0 16.2
## 25 15.9 15.3 23.6 15.8 16.4 14.9 13.6 15.0 16.5 0.0
That’s a little confusing. Let’s keep the restaurant names and take a look at this again. We will print it as a massive table.
# Extract restaurant names
rest_names <- ff_pts[["Restaurant Name"]] # or ff_pts$`Restaurant Name`
# Assign names to rows and columns of distance and duration matrices
rownames(ff_table$duration) <- rest_names
colnames(ff_table$duration) <- rest_names
# Travel times in minutes
ff_table$duration
## In-N-Out Burger Dairy Queen Carl's Jr.
## In-N-Out Burger 0.0 6.3 13.4
## Dairy Queen 6.7 0.0 9.3
## Carl's Jr. 13.5 9.4 0.0
## Chipotle Mexican Grill 4.0 4.7 13.2
## Subway 19.5 20.8 19.8
## Taco Bell 5.2 1.8 10.6
## KFC 3.7 8.6 14.6
## Jack in the Box 4.7 2.2 11.0
## Del Taco 3.9 8.8 14.2
## Mr. Pickle's Sandwich Shop 7.0 9.0 10.9
## ChickPeas 15.1 12.4 9.7
## Panda Express 18.3 15.5 12.8
## Burger King 21.4 18.6 15.9
## McDonald's 17.9 19.4 19.1
## Baja Fresh 3.7 4.2 12.8
## Teaspoon 2.7 4.1 12.6
## Burgers & Brew 4.4 4.5 13.0
## Teabo Cafe 7.1 9.1 11.0
## Hunan Bar & Restaurant 3.2 4.3 12.8
## Raising Cane's Chicken Fingers 3.1 4.5 13.0
## Wingstop 4.9 1.8 10.5
## Domino's Pizza 6.8 2.8 9.3
## Steve's Pizza 4.4 2.9 11.4
## The Hotdogger 3.1 5.0 13.4
## Togo's Eatery 18.6 15.8 13.1
## Chipotle Mexican Grill Subway Taco Bell KFC
## In-N-Out Burger 3.9 18.2 5.0 5.5
## Dairy Queen 4.7 20.6 1.8 10.1
## Carl's Jr. 13.3 19.8 10.6 14.2
## Chipotle Mexican Grill 0.0 21.2 3.3 7.4
## Subway 22.0 0.0 22.0 16.9
## Taco Bell 3.2 21.8 0.0 8.6
## KFC 6.2 16.8 7.3 0.0
## Jack in the Box 2.8 21.6 0.9 8.2
## Del Taco 6.4 16.5 7.5 1.1
## Mr. Pickle's Sandwich Shop 9.5 14.1 10.2 4.8
## ChickPeas 12.7 28.8 11.5 18.5
## Panda Express 16.2 31.9 14.5 21.7
## Burger King 19.3 35.0 17.7 24.8
## McDonald's 20.4 2.2 20.6 15.2
## Baja Fresh 1.4 21.0 2.9 7.2
## Teaspoon 2.4 20.0 2.7 6.2
## Burgers & Brew 2.1 21.7 3.2 7.8
## Teabo Cafe 9.6 14.0 10.3 4.9
## Hunan Bar & Restaurant 1.4 20.5 3.0 6.7
## Raising Cane's Chicken Fingers 1.2 20.4 3.1 6.5
## Wingstop 2.9 21.7 0.4 8.3
## Domino's Pizza 4.8 23.0 1.9 10.2
## Steve's Pizza 2.4 21.6 1.6 7.8
## The Hotdogger 2.1 20.4 3.7 6.6
## Togo's Eatery 16.5 32.2 14.9 22.0
## Jack in the Box Del Taco
## In-N-Out Burger 4.4 5.5
## Dairy Queen 2.3 10.1
## Carl's Jr. 11.0 14.1
## Chipotle Mexican Grill 2.7 7.3
## Subway 21.9 16.5
## Taco Bell 0.8 8.6
## KFC 6.7 1.2
## Jack in the Box 0.0 8.1
## Del Taco 6.9 0.0
## Mr. Pickle's Sandwich Shop 10.0 4.8
## ChickPeas 11.8 18.5
## Panda Express 14.8 21.7
## Burger King 18.0 24.8
## McDonald's 20.9 14.8
## Baja Fresh 2.3 7.1
## Teaspoon 2.1 6.1
## Burgers & Brew 2.7 7.8
## Teabo Cafe 10.1 4.9
## Hunan Bar & Restaurant 2.4 6.7
## Raising Cane's Chicken Fingers 2.5 6.5
## Wingstop 0.4 8.2
## Domino's Pizza 2.4 10.2
## Steve's Pizza 0.8 7.7
## The Hotdogger 3.0 6.6
## Togo's Eatery 15.2 22.0
## Mr. Pickle's Sandwich Shop ChickPeas
## In-N-Out Burger 7.6 14.8
## Dairy Queen 8.9 12.7
## Carl's Jr. 11.1 10.0
## Chipotle Mexican Grill 9.4 12.6
## Subway 14.0 29.4
## Taco Bell 10.1 11.9
## KFC 5.0 17.1
## Jack in the Box 10.2 12.1
## Del Taco 4.5 17.3
## Mr. Pickle's Sandwich Shop 0.0 20.4
## ChickPeas 20.1 0.0
## Panda Express 23.2 3.9
## Burger King 26.3 7.0
## McDonald's 12.4 28.6
## Baja Fresh 9.3 11.7
## Teaspoon 8.2 12.9
## Burgers & Brew 9.9 11.3
## Teabo Cafe 0.1 20.5
## Hunan Bar & Restaurant 8.8 11.8
## Raising Cane's Chicken Fingers 8.6 12.7
## Wingstop 10.1 11.8
## Domino's Pizza 11.3 10.5
## Steve's Pizza 9.8 12.2
## The Hotdogger 8.7 12.5
## Togo's Eatery 23.5 4.3
## Panda Express Burger King McDonald's Baja Fresh
## In-N-Out Burger 18.4 21.1 18.3 3.6
## Dairy Queen 15.9 18.7 19.7 4.2
## Carl's Jr. 13.0 15.7 19.3 12.8
## Chipotle Mexican Grill 16.5 19.0 20.7 1.4
## Subway 32.3 35.1 2.2 21.8
## Taco Bell 15.1 17.9 20.9 2.8
## KFC 20.6 23.4 15.5 6.0
## Jack in the Box 15.3 18.1 21.1 2.3
## Del Taco 20.9 23.6 15.2 6.2
## Mr. Pickle's Sandwich Shop 23.3 26.1 12.8 9.4
## ChickPeas 4.2 7.0 28.3 11.8
## Panda Express 0.0 3.3 31.4 15.3
## Burger King 3.8 0.0 34.5 18.4
## McDonald's 31.5 34.3 0.0 20.2
## Baja Fresh 15.7 18.2 20.5 0.0
## Teaspoon 16.1 18.9 19.5 2.1
## Burgers & Brew 15.2 17.7 21.1 1.2
## Teabo Cafe 23.4 26.2 12.7 9.5
## Hunan Bar & Restaurant 15.7 18.2 20.0 0.5
## Raising Cane's Chicken Fingers 16.7 19.2 19.8 1.5
## Wingstop 15.1 17.9 20.9 2.4
## Domino's Pizza 13.7 16.5 22.0 4.3
## Steve's Pizza 15.4 18.1 21.1 1.9
## The Hotdogger 16.4 18.9 19.9 1.2
## Togo's Eatery 0.7 4.0 31.7 15.6
## Teaspoon Burgers & Brew Teabo Cafe
## In-N-Out Burger 2.3 4.2 7.7
## Dairy Queen 4.1 4.6 8.9
## Carl's Jr. 12.6 13.1 11.2
## Chipotle Mexican Grill 2.4 2.0 9.5
## Subway 20.4 22.4 14.0
## Taco Bell 2.7 3.1 10.2
## KFC 4.5 6.5 5.1
## Jack in the Box 2.2 2.6 10.3
## Del Taco 4.8 6.8 4.6
## Mr. Pickle's Sandwich Shop 7.9 9.9 0.1
## ChickPeas 12.5 11.5 20.2
## Panda Express 15.6 15.0 23.3
## Burger King 18.7 18.1 26.4
## McDonald's 18.7 20.8 12.4
## Baja Fresh 2.0 1.2 9.4
## Teaspoon 0.0 2.6 8.3
## Burgers & Brew 2.7 0.0 10.0
## Teabo Cafe 8.0 10.0 0.0
## Hunan Bar & Restaurant 1.6 1.2 8.9
## Raising Cane's Chicken Fingers 1.3 2.1 8.7
## Wingstop 2.3 2.7 10.2
## Domino's Pizza 4.2 4.7 11.3
## Steve's Pizza 1.7 2.2 9.9
## The Hotdogger 1.8 1.9 8.8
## Togo's Eatery 15.9 15.3 23.6
## Hunan Bar & Restaurant
## In-N-Out Burger 3.1
## Dairy Queen 4.4
## Carl's Jr. 12.9
## Chipotle Mexican Grill 1.5
## Subway 21.3
## Taco Bell 2.9
## KFC 5.5
## Jack in the Box 2.4
## Del Taco 5.7
## Mr. Pickle's Sandwich Shop 8.8
## ChickPeas 12.0
## Panda Express 15.4
## Burger King 18.5
## McDonald's 19.7
## Baja Fresh 0.5
## Teaspoon 1.5
## Burgers & Brew 1.3
## Teabo Cafe 8.9
## Hunan Bar & Restaurant 0.0
## Raising Cane's Chicken Fingers 1.0
## Wingstop 2.5
## Domino's Pizza 4.5
## Steve's Pizza 2.1
## The Hotdogger 0.7
## Togo's Eatery 15.8
## Raising Cane's Chicken Fingers Wingstop
## In-N-Out Burger 3.0 4.5
## Dairy Queen 4.6 1.9
## Carl's Jr. 12.9 10.7
## Chipotle Mexican Grill 1.2 2.9
## Subway 21.1 21.9
## Taco Bell 3.1 0.4
## KFC 5.3 6.8
## Jack in the Box 2.5 0.4
## Del Taco 5.5 7.0
## Mr. Pickle's Sandwich Shop 8.6 10.2
## ChickPeas 12.9 11.5
## Panda Express 16.0 14.6
## Burger King 19.1 17.7
## McDonald's 19.5 20.7
## Baja Fresh 1.6 2.5
## Teaspoon 1.3 2.3
## Burgers & Brew 2.3 2.8
## Teabo Cafe 8.7 10.3
## Hunan Bar & Restaurant 1.1 2.5
## Raising Cane's Chicken Fingers 0.0 2.7
## Wingstop 2.7 0.0
## Domino's Pizza 4.6 2.0
## Steve's Pizza 2.1 1.1
## The Hotdogger 1.4 3.2
## Togo's Eatery 16.4 14.9
## Domino's Pizza Steve's Pizza The Hotdogger
## In-N-Out Burger 6.5 3.6 2.9
## Dairy Queen 2.8 3.2 5.1
## Carl's Jr. 9.5 11.7 13.6
## Chipotle Mexican Grill 4.8 2.1 2.1
## Subway 23.1 21.7 21.1
## Taco Bell 2.0 1.7 3.7
## KFC 8.7 5.9 5.3
## Jack in the Box 2.4 1.1 3.1
## Del Taco 8.9 6.1 5.5
## Mr. Pickle's Sandwich Shop 11.3 9.3 8.7
## ChickPeas 10.1 11.6 12.7
## Panda Express 13.2 14.7 16.2
## Burger King 16.3 17.8 19.3
## McDonald's 21.7 20.1 19.5
## Baja Fresh 4.4 1.7 1.2
## Teaspoon 4.2 1.4 1.7
## Burgers & Brew 4.7 2.1 2.1
## Teabo Cafe 11.4 9.4 8.8
## Hunan Bar & Restaurant 4.5 1.8 0.8
## Raising Cane's Chicken Fingers 4.7 1.9 1.4
## Wingstop 2.0 1.3 3.3
## Domino's Pizza 0.0 3.2 5.2
## Steve's Pizza 3.1 0.0 2.8
## The Hotdogger 5.2 2.4 0.0
## Togo's Eatery 13.6 15.0 16.5
## Togo's Eatery
## In-N-Out Burger 18.2
## Dairy Queen 15.7
## Carl's Jr. 12.8
## Chipotle Mexican Grill 16.3
## Subway 32.1
## Taco Bell 14.9
## KFC 20.5
## Jack in the Box 15.1
## Del Taco 20.7
## Mr. Pickle's Sandwich Shop 23.1
## ChickPeas 4.0
## Panda Express 1.2
## Burger King 4.6
## McDonald's 31.3
## Baja Fresh 15.5
## Teaspoon 15.9
## Burgers & Brew 15.0
## Teabo Cafe 23.2
## Hunan Bar & Restaurant 15.5
## Raising Cane's Chicken Fingers 16.5
## Wingstop 14.9
## Domino's Pizza 13.5
## Steve's Pizza 15.2
## The Hotdogger 16.2
## Togo's Eatery 0.0
So let’s try something different with network analyses. What if I wanted to create a map that would show me all the locations I could reach within a given number of minutes of biking? This is called an isochrone and you betcha the osrm package can do this!
First, let’s set this up to be centered around the building we all know and love, the one that we are in right now. That’s right–the epically named Medical Sciences 1-B. I’ve clearly memorized the longitude and latitude for this building, which we paste in below, and then store as an sf object.
loc <- c("lon" = -121.7617, "lat" = 38.5382)
loc_sf <- st_sf(name = "Medical Sciences 1-B",
geometry = st_sfc(st_point(loc), crs = 4326))
Next, we use the osrmIsochrone() function to create the
isochrones. We will specify the loc to be loc_sf, the
breaks (in minutes) will be 0 to 30 minutes by 5 minute
intervals, the res or resolution will be 30, and the
osrm.profile, or the mode, will be “bike”. Let’s run it!
iso <- osrmIsochrone(loc = loc_sf,
breaks = seq(0, 30, by = 5), # 0–30 mins in 5 min intervals
res = 30, # Lower res for faster requests
osrm.profile = "bike") # mode bicycle
glimpse(iso)
## Rows: 6
## Columns: 4
## $ id <int> 1, 2, 3, 4, 5, 6
## $ isomin <dbl> 0, 5, 10, 15, 20, 25
## $ isomax <dbl> 5, 10, 15, 20, 25, 30
## $ geometry <MULTIPOLYGON [°]> MULTIPOLYGON (((-121.7586 3..., MULTIPOLYGON (((-121.7538 3..…
OK, so now we can see we have isomin and isomax as our columns for our time bins. And our dataset is a MULTIPOLYGON, so it’s spatial. Let’s map this! First, we will create a categorical variable called run_times. Then we will use tmap to plot our isochrones. We will use a palette of heat colors for our ischrones, and we will also plot Medical Sciences 1-B in there just so we never forget our beautiful home base.
# Create factor column for time intervals
iso$run_times <- factor(
paste(iso$isomin, "to", iso$isomax, "min"),
levels = c("0 to 5 min", "5 to 10 min", "10 to 15 min",
"15 to 20 min", "20 to 25 min", "25 to 30 min")
)
# Plot using tmap
tm <- tm_shape(iso) +
tm_polygons(col = "run_times",
palette = rev(heat.colors(6)), # reverse for same effect
border.col = "black",
lwd = 0.5,
alpha = 0.3,
title = "Biking Duration") +
tm_shape(loc_sf) +
tm_bubbles(size = 0.3, col = "blue") +
tm_text("name", size = 1, ymod = -0.5)
##
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_tm_polygons()`: migrate the argument(s) related to the scale of
## the visual variable `fill` namely 'palette' (rename to 'values') to fill.scale
## = tm_scale(<HERE>).
## [v3->v4] `tm_polygons()`: use 'fill' for the fill color of polygons/symbols
## (instead of 'col'), and 'col' for the outlines (instead of 'border.col').
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
## [v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'
tm
Would you look at that! It’s like a work of art. Georgia O’Keefe, eat your heart out. Click around and explore where you could bike to in 10 minutes once class gets out! And you’ve earned that osrm badge! Get biking somewhere–you know the route now!
—