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:

  1. Use the osrm package to map the optimal trip between multiple points
  2. Get a matrix of travel times between multiple points
  3. Create an isochrone to map travel times from one location

Let’s get rollin…


Load Packages

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


osrm package for network analysis in R

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.


Bring in fast food establishments in Davis, CA

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)


osrmTrip to map the optimal trip between all fast food establishments

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


osrmTable to get a matrix of travel times between all fast food establishments in Davis

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

osrmIsochrone to create isochrones

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!


Your osrm Badge!!

LS0tCnRpdGxlOiAnTGFiIDc6IE5ldHdvcmsgQW5hbHlzaXMnCi0tLQoKSW4gdGhpcyBsYWIsIHdlIHdpbGwgYmUgdXNpbmcgc3BhdGlhbCBkYXRhIHRvIHJ1biBhIG5ldHdvcmsgYW5hbHlzaXMuIFdlIHdpbGwgYmUgcGxheWluZyBhcm91bmQgd2l0aCBkYXRhIGZyb20gRGF2aXMsIGluY2x1ZGluZyBnZW9jb2RpbmcgZmFzdCBmb29kIHJlc3RhdXJhbnRzIGluIG91ciBmYWlyIGNpdHksIHRoZW4gbWFwcGluZyB0aGUgbW9zdCBlZmZpY2llbnQgcm91dGUgdG8gYmlrZSBiZXR3ZWVuIGFsbCBmYXN0IGZvb2QgZXN0YWJsaXNobWVudHMuIE5leHQsIHdlIHdpbGwgZXN0aW1hdGUgdGhlIHRyYXZlbCB0aW1lIGJldHdlZW4gYWxsIGZhc3QgZm9vZCBlc3RhYmxpc2htZW50cyBpbiBEYXZpcy4gRmluYWxseSwgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIGNvb2wgdmlzdWFsaXphdGlvbiBvZiB0aGUgdHJhdmVsIHRpbWVzIGZyb20gdGhlIGJ1aWxkaW5nIHRoYXQgd2UgYXJlIHNpdHRpbmcgaW4hIFRoaXMgaXMgYWxsIGludGVuZGVkIHRvIGV4cGxhaW4gaG93IHlvdSBjYW4gdXNlIHNwYXRpYWwgZGF0YSB0byB1bmRlcnN0YW5kIGhvdyBkaWZmZXJlbnQgc3BhdGlhbCBmZWF0dXJlcyByZWxhdGUgdXNpbmcgdGhlIHJvYWQgbmV0d29ya3MgdGhhdCB3ZSB0cmF2ZWwgb24uCgpUaGUgb2JqZWN0aXZlcyBvZiB0aGlzIGd1aWRlIGFyZSB0byB0ZWFjaCB5b3UgdG86CgoxLiBVc2UgdGhlICoqb3NybSoqIHBhY2thZ2UgdG8gbWFwIHRoZSBvcHRpbWFsIHRyaXAgYmV0d2VlbiBtdWx0aXBsZSBwb2ludHMKMi4gR2V0IGEgbWF0cml4IG9mIHRyYXZlbCB0aW1lcyBiZXR3ZWVuIG11bHRpcGxlIHBvaW50cwozLiBDcmVhdGUgYW4gaXNvY2hyb25lIHRvIG1hcCB0cmF2ZWwgdGltZXMgZnJvbSBvbmUgbG9jYXRpb24KCkxldCdzIGdldCByb2xsaW4uLi4KClwKCiMgTG9hZCBQYWNrYWdlcwpGaXJzdCwgd2UgYWx3YXlzIGdvdHRhIHJlbWVtYmVyIHRvIGxvYWQgb3VyIHBhY2thZ2VzLgpgYGB7cn0KbGlicmFyeSh0aWR5Z2VvY29kZXIpCmxpYnJhcnkoc2YpCmxpYnJhcnkodG1hcCkKbGlicmFyeShvc3JtKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpcCgojIG9zcm0gcGFja2FnZSBmb3IgbmV0d29yayBhbmFseXNpcyBpbiBSClIgb2ZmZXJzIGEgZmV3IHBhY2thZ2VzIGZvciBuZXR3b3JrIGFuYWx5c2lzLiBXZSB3aWxsIGJlIHVzaW5nIHRoZSAqKm9zcm0qKiBwYWNrYWdlIGJlY2F1c2UgaXQgaXMgZmFzdCBhbmQgb3BlbiBzb3VyY2UuIEl0IGlzIGFuIGludGVyZmFjZSBiZXR3ZWVuIFIgYW5kIHRoZSBPcGVuIFN0cmVldCBNYXAtQmFzZWQgUm91dGluZyBTZXJ2aWNlLiBJdCBoYXMgYSBudW1iZXIgb2Ygb3B0aW9ucywgaW5jbHVkaW5nOgoKLSBgb3NybVRyaXBgOiBCdWlsZCBhbmQgc2VuZCBhbiBPU1JNIEFQSSBxdWVyeSB0byBnZXQgdGhlIHNob3J0ZXN0IHRyYXZlbCBnZW9tZXRyeSBiZXR3ZWVuIG11bHRpcGxlIHVub3JkZXJlZCBwb2ludHMuIFRoaXMgZnVuY3Rpb24gaW50ZXJmYWNlcyB0aGUgdHJpcCBPU1JNIHNlcnZpY2UuIFVzZSB0aGlzIGZ1bmN0aW9uIHRvIHJlc29sdmUgdGhlIHRyYXZlbGxpbmcgc2FsZXNtYW4gcHJvYmxlbS4KLSBgb3NybVRhYmxlYDogQnVpbGQgYW5kIHNlbmQgT1NSTSBBUEkgcXVlcmllcyB0byBnZXQgdHJhdmVsIHRpbWUgbWF0cmljZXMgYmV0d2VlbiBwb2ludHMuIFRoaXMgZnVuY3Rpb24gaW50ZXJmYWNlcyB0aGUgdGFibGUgT1NSTSBzZXJ2aWNlLgotIGBvc3JtSXNvY2hyb25lYDogVGhpcyBmdW5jdGlvbiBjb21wdXRlcyBhcmVhcyB0aGF0IGFyZSByZWFjaGFibGUgd2l0aGluIGEgZ2l2ZW4gdGltZSBzcGFuIGZyb20gYSBwb2ludCBhbmQgcmV0dXJucyB0aGUgcmVhY2hhYmxlIHJlZ2lvbnMgYXMgcG9seWdvbnMuIFRoZXNlIGFyZWFzIG9mIGVxdWFsIHRyYXZlbCB0aW1lIGFyZSBjYWxsZWQgaXNvY2hyb25lcy4KLSBgb3NybVJvdXRlYDogQnVpbGQgYW5kIHNlbmQgYW4gT1NSTSBBUEkgcXVlcnkgdG8gZ2V0IHRoZSB0cmF2ZWwgZ2VvbWV0cnkgYmV0d2VlbiB0d28gcG9pbnRzLiBUaGlzIGZ1bmN0aW9uIGludGVyZmFjZXMgd2l0aCB0aGUgcm91dGUgT1NSTSBzZXJ2aWNlLgotIGBvc3JtTmVhcmVzdGA6IEJ1aWxkIGFuZCBzZW5kIGFuIE9TUk0gQVBJIHF1ZXJ5IHRvIGdldCB0aGUgbmVhcmVzdCBwb2ludCBvbiB0aGUgc3RyZWV0IG5ldHdvcmsuIFRoaXMgZnVuY3Rpb24gaW50ZXJmYWNlcyB0aGUgbmVhcmVzdCBPU1JNIHNlcnZpY2UuCi0gYG9zcm1Jc29kaXN0YW5jZWA6IFRoaXMgZnVuY3Rpb24gY29tcHV0ZXMgYXJlYXMgdGhhdCBhcmUgcmVhY2hhYmxlIHdpdGhpbiBhIGdpdmVuIHJvYWQgZGlzdGFuY2UgZnJvbSBhIHBvaW50IGFuZCByZXR1cm5zIHRoZSByZWFjaGFibGUgcmVnaW9ucyBhcyBwb2x5Z29ucy4gVGhlc2UgYXJlYXMgb2YgZXF1YWwgdHJhdmVsIGRpc3RhbmNlIGFyZSBjYWxsZWQgaXNvZGlzdGFuY2VzLgoKXAoKTm90ZSB0aGF0IHRoZXJlIGFyZSBvdGhlciBvcHRpb25zIGZvciBuZXR3b3JrIGFuYWx5c2VzIGluIFIsIGluY2x1ZGluZyAqKmdtYXBzZGlzdGFuY2UqKiwgd2hpY2ggdXNlcyB0aGUgR29vZ2xlIE1hcHMgQVBJLCAqKnN0cGxhbnIqKiwgYW5kIG90aGVycy4gQnV0IG1hbnkgb2YgdGhlc2UgcmVxdWlyZSBhbiBBUEkga2V5IGFuZCBtYXkgZXZlbiBjb3N0IG1vbmV5IGlmIHlvdSB1c2UgdGhlbSB0b28gbXVjaCEgU28gZm9yIG5vdywgd2Ugd2lsbCBzdGljayB0byAqKm9zcm0qKi4KClwKCiMgQnJpbmcgaW4gZmFzdCBmb29kIGVzdGFibGlzaG1lbnRzIGluIERhdmlzLCBDQQpTaW1pbGFyIHRvIHdoYXQgd2UgZGlkIGluIG9uZSBvZiBvdXIgZmlyc3QgbGFicywgbGV0J3MgYnJpbmcgaW4gdGhlIGFkZHJlc3NlcyBvZiBhbGwgZmFzdCBmb29kIGVzdGFibGlzaG1lbnRzIGluIERhdmlzIGFuZCBnZW9jb2RlIHRoZW0hIFdlIHdpbGwgZmlyc3QgcmVhZCBpbiB0aGUgLmNzdiBmcm9tIHRoZSB3ZWJzaXRlIHVzaW5nIGByZWFkX2NzdmAgZnJvbSB0aGUgKip0aWR5dmVyc2UqKiBwYWNrYWdlLgpgYGB7cn0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3BqYW1lcy11Y2RhdmlzL1NQSDIxNS9yZWZzL2hlYWRzL21haW4vZmFzdF9mb29kX2RhdmlzX2NhLmNzdiIsIGRlc3RmaWxlID0gImZhc3RfZm9vZF9kYXZpc19jYS5jc3YiLCBtb2RlID0gIndiIikKZmYgPC0gcmVhZF9jc3YoImZhc3RfZm9vZF9kYXZpc19jYS5jc3YiKQoKZ2xpbXBzZShmZikKYGBgCgpcCgpPSywgc28gbG9va3MgbGlrZSAqQWRkcmVzcyogaXMgdGhlIHZhcmlhYmxlIHdlIHdhbnQgdG8gZm9jdXMgb24uIExldCdzIGdlb2NvZGUgdGhlIERhdmlzIGZhc3QgZm9vZCBkYXRhIHVzaW5nIHRoZSB0aGUgYGdlb2NvZGUoKWAgZnVuY3Rpb24gaW4gKip0aWR5Z2VvY29kZXIqKi4gQW5kIGxldCdzIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIHVzZSB0aGUgImFyY2dpcyIgZ2VvY29kZXIuCmBgYHtyfQpmZl9nZW8gICAgICA8LSBnZW9jb2RlKGZmLCBhZGRyZXNzID0gIkFkZHJlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJhcmNnaXMiKQpgYGAKClwKCkFscmlnaHR5IHRoYXQgcmFuISBOb3cgbGV0J3MgY2hlY2sgb3VyIGRhdGEgYW5kIG1ha2Ugc3VyZSB0aGVyZSdzIG5vIG1pc3NpbmduZXNzIGluIG91ciAqbGF0KiBhbmQgKmxvbmcqIGZpZWxkcy4KYGBge3J9CnN1bW1hcnkoZmZfZ2VvJGxhdCkKc3VtbWFyeShmZl9nZW8kbG9uZykKYGBgCgpcCgpMb29rcyBsaWtlIHRoZXJlJ3Mgbm8gbWlzc2luZ25lc3MhIE5vIE5BcyEgT0ssIG5vdyB3ZSB1c2UgYHN0X2FzX3NmYCBmcm9tIHRoZSAqKnNmKiogcGFja2FnZSB0byBtYWtlIHRoZSBkYXRhc2V0IHNwYXRpYWwgYW5kIHdlIHVzZSB0aGUgQ1JTIG9mIDQzMjYgYmVjYXVzZSB3ZSBoYXZlIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUuCmBgYHtyfQpmZl9wdHMgPC0gc3RfYXNfc2YoZmZfZ2VvLCBjb29yZHM9YygibG9uZyIsImxhdCIpLCBjcnM9NDMyNikKYGBgCgpcCgojIG9zcm1UcmlwIHRvIG1hcCB0aGUgb3B0aW1hbCB0cmlwIGJldHdlZW4gYWxsIGZhc3QgZm9vZCBlc3RhYmxpc2htZW50cwoKTm93IGxldCdzIHVzZSB0aGUgKipvc3JtKiogcGFja2FnZSB0byBjYWxjdWxhdGUgYW4gb3B0aW1hbCB0cmlwIGJldHdlZW4gYWxsIGZhc3QgZm9vZCBlc3RhYmxpc2htZW50cyBpbiBEYXZpcywgdGhlbiBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgb3V0cHV0LiBUaGlzIG1lYW5zIHdlIGFyZSBlc3RpbWF0aW5nIHRoZSBzaG9ydGVzdCB0cmF2ZWwgZ2VvbWV0cnkgYmV0d2VlbiBhbGwgZmFzdCBmb29kIGVzdGFibGlzaG1lbnRzIGluIERhdmlzLiBBbmQgd2UgYXJlIGluIERhdmlzLCBzbyBvZiBjb3Vyc2Ugd2Ugd2lsbCBlc3RpbWF0ZSB0aGlzIG9uIGJpa2UhIE1heWJlIHVzZWZ1bCBmb3IgYW4gU1BIIDIxNSBwdWIgY3Jhd2wgaW4gdGhlIGZ1dHVyZT8KYGBge3J9CnRyaXAgPC0gb3NybVRyaXAoZmZfcHRzLCBvc3JtLnByb2ZpbGUgPSAiYmlrZSIpCmdsaW1wc2UodHJpcCkKYGBgCgpcCgpPSywgaXQgcmFuISBTb21lIGdvb2QgaW5mbyBpbiB0aGVyZS4gTG9va3MgbGlrZSB3ZSBoYXZlIGEgKkxpc3QqIHdpdGggYW4gc2YgZGF0YXNldCwgYSBkYXRhLmZyYW1lLCBhbmQgYSBsaXN0IG9mIGR1cmF0aW9uIGFuZCBkaXN0YW5jZS4gTm93LCBsZXQncyB2aXN1YWxpemUgd2hhdCB0aGlzIHBhdGggbWlnaHQgbG9vayBsaWtlIHVzaW5nIHRoZSAqKnRtYXAqKiBwYWNrYWdlLiBXZSB3aWxsIHB1bGwgb3V0IHRoZSByb3V0ZSBhcyBhbiAqKnNmKiogb2JqZWN0IGFuZCBzdG9yZSBpdCBhcyAqdHJpcF9zZiouIFRoZW4gd2Ugd2lsbCBtYXAgdGhhdCBhbmQgc3BlY2lmeSB0aGF0IHdlIHdhbnQgdGhlIGxpbmVzIHRvIGJlIHRoZSByb3V0ZSBpbiBibHVlLCBhbmQgdGhlbiB3ZSB3YW50IHRvIHNlZSB0aGUgbG9jYXRpb24gb2Ygb3VyIGZhc3QgZm9vZCBlc3RhYmxpc2htZW50cyAqZmZfcHRzKiBpbiByZWQgYnViYmxlcy4gV2UgYWxzbyBjYW4gbGFiZWwgdGhlIHJlc3RhdXJhbnRzIHVzaW5nIGB0bV90ZXh0YCBhbmQgc2V0IGFuIG9mZnNldCB3aXRoIGB4bW9kYCBhbmQgYHltb2RgIG9wdGlvbnMuCmBgYHtyfQojIEV4dHJhY3QgdGhlIGxpbmVzdHJpbmcgcm91dGUgKGFzIGFuIHNmIG9iamVjdCkKdHJpcF9zZiA8LSB0cmlwW1sxXV0kdHJpcAoKIyBTZXQgdG1hcCB0byBpbnRlcmFjdGl2ZSBtb2RlCnRtYXBfbW9kZSgidmlldyIpCgojIFBsb3Qgd2l0aCB0bWFwCnRtIDwtIHRtX3NoYXBlKHRyaXBfc2YpICsKICB0bV9saW5lcyhsd2QgPSAzLCBjb2wgPSAiYmx1ZSIpICsKICB0bV9zaGFwZShmZl9wdHMpICsKICB0bV9idWJibGVzKHNpemUgPSAwLjUsIGNvbCA9ICJyZWQiKSArIAogIHRtX3RleHQoIlJlc3RhdXJhbnQgTmFtZSIsIHNpemU9MSwgeG1vZCA9IDAuMjUsIHltb2Q9MC4yNSkgCgp0bQoKYGBgCgpcCgpTcGVjdGFjdWxhciEgTGV0J3Mgbm93IGdldCBzb21lIHN1bW1hcnkgc3RhdHMgZm9yIGVhY2ggbGVnIG9mIG91ciBmYXN0IGZvb2QgZXh0cmF2YWdhbnphLiBXZSBjYW4gdXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIGZyb20gKip0aWR5dmVyc2UqKiB0byBnZXQgdGhpcyBpbmZvLgpgYGB7cn0KbGVnX3N1bW1hcnkgPC0gdHJpcF9zZiAlPiUKICBtdXRhdGUoZHVyYXRpb24gPSByb3VuZChkdXJhdGlvbiwgMSksCiAgICAgICAgIGRpc3RhbmNlID0gcm91bmQoZGlzdGFuY2UsIDEpKQoKbGVnX3N1bW1hcnkKYGBgCgpcCgpPSywgd2Ugd2UgaGF2ZSB0aGUgZHVyYXRpb24gYW5kIGRpc3RhbmNlIGZvciBlYWNoIHRyaXAuIExldCdzIGdldCB0aGUgc3VtbWFyeSBvZiB0aGUgd2hvbGUgdHJpcCBpbiBtaW51dGVzIGFuZCBraWxvbWV0ZXJzLgpgYGB7cn0KdHJpcF9zdW1tYXJ5IDwtIHRyaXBbWzFdXSRzdW1tYXJ5CnRyaXBfc3VtbWFyeQpgYGAKXAoKIyBvc3JtVGFibGUgdG8gZ2V0IGEgbWF0cml4IG9mIHRyYXZlbCB0aW1lcyBiZXR3ZWVuIGFsbCBmYXN0IGZvb2QgZXN0YWJsaXNobWVudHMgaW4gRGF2aXMKClNvIGl0IHdhcyBuaWNlIHRvIGdldCBhbiBvcHRpbWFsIHJvdXRlLCBidXQgd2hhdCBpZiBJIHdhbnRlZCB0aGUgdHJhdmVsIHRpbWVzIGJldHdlZW4gYWxsIG9mIHRoZSBmYXN0IGZvb2QgZXN0YWJsaXNobWVudHMgaW4gRGF2aXM/IFlvdSBjb3VsZCBpbWFnaW5lIGRvaW5nIHRoaXMgd2l0aCBhZGRyZXNzZXMgb2YgZ2VvY29kZWQgcGFydGljaXBhbnRzIGFuZCBmYXN0IGZvb2QgZXN0YWJsaXNobWVudHMgdG8gY3JlYXRlIGEgZGF0YXNldCB3aXRoIGRpc3RhbmNlIHRvIHRoZSBjbG9zZXN0IGZhc3QgZm9vZCBlc3RhYmxpc2htZW50LgoKYGBge3J9CmZmX3RhYmxlIDwtIG9zcm1UYWJsZShmZl9wdHMsCiAgICAgICAgICAgICAgICAgICAgICBvc3JtLnByb2ZpbGUgPSAiYmlrZSIpCmdsaW1wc2UoZmZfdGFibGUpCgpgYGAKClwKCkdyZWF0LiBOb3cgbGV0J3MgbG9vayBhdCB0aGUgKmR1cmF0aW9uKiBtYXRyaXguCmBgYHtyfQpmZl90YWJsZSRkdXJhdGlvbgpgYGAKClwKClRoYXQncyBhIGxpdHRsZSBjb25mdXNpbmcuIExldCdzIGtlZXAgdGhlIHJlc3RhdXJhbnQgbmFtZXMgYW5kIHRha2UgYSBsb29rIGF0IHRoaXMgYWdhaW4uIFdlIHdpbGwgcHJpbnQgaXQgYXMgYSBtYXNzaXZlIHRhYmxlLgpgYGB7cn0KIyBFeHRyYWN0IHJlc3RhdXJhbnQgbmFtZXMKcmVzdF9uYW1lcyA8LSBmZl9wdHNbWyJSZXN0YXVyYW50IE5hbWUiXV0gICMgb3IgZmZfcHRzJGBSZXN0YXVyYW50IE5hbWVgCgojIEFzc2lnbiBuYW1lcyB0byByb3dzIGFuZCBjb2x1bW5zIG9mIGRpc3RhbmNlIGFuZCBkdXJhdGlvbiBtYXRyaWNlcwpyb3duYW1lcyhmZl90YWJsZSRkdXJhdGlvbikgPC0gcmVzdF9uYW1lcwpjb2xuYW1lcyhmZl90YWJsZSRkdXJhdGlvbikgPC0gcmVzdF9uYW1lcwoKIyBUcmF2ZWwgdGltZXMgaW4gbWludXRlcwpmZl90YWJsZSRkdXJhdGlvbgpgYGAKCiMgb3NybUlzb2Nocm9uZSB0byBjcmVhdGUgaXNvY2hyb25lcwoKU28gbGV0J3MgdHJ5IHNvbWV0aGluZyBkaWZmZXJlbnQgd2l0aCBuZXR3b3JrIGFuYWx5c2VzLiBXaGF0IGlmIEkgd2FudGVkIHRvIGNyZWF0ZSBhIG1hcCB0aGF0IHdvdWxkIHNob3cgbWUgYWxsIHRoZSBsb2NhdGlvbnMgSSBjb3VsZCByZWFjaCB3aXRoaW4gYSBnaXZlbiBudW1iZXIgb2YgbWludXRlcyBvZiBiaWtpbmc/IFRoaXMgaXMgY2FsbGVkIGFuICoqaXNvY2hyb25lKiogYW5kIHlvdSBiZXRjaGEgdGhlICoqb3NybSoqIHBhY2thZ2UgY2FuIGRvIHRoaXMhCgpGaXJzdCwgbGV0J3Mgc2V0IHRoaXMgdXAgdG8gYmUgY2VudGVyZWQgYXJvdW5kIHRoZSBidWlsZGluZyB3ZSBhbGwga25vdyBhbmQgbG92ZSwgdGhlIG9uZSB0aGF0IHdlIGFyZSBpbiByaWdodCBub3cuIFRoYXQncyByaWdodC0tdGhlIGVwaWNhbGx5IG5hbWVkIE1lZGljYWwgU2NpZW5jZXMgMS1CLiBJJ3ZlIGNsZWFybHkgbWVtb3JpemVkIHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGZvciB0aGlzIGJ1aWxkaW5nLCB3aGljaCB3ZSBwYXN0ZSBpbiBiZWxvdywgYW5kIHRoZW4gc3RvcmUgYXMgYW4gKipzZioqIG9iamVjdC4KYGBge3J9CmxvYyA8LSBjKCJsb24iID0gLTEyMS43NjE3LCAibGF0IiA9IDM4LjUzODIpCmxvY19zZiA8LSBzdF9zZihuYW1lID0gIk1lZGljYWwgU2NpZW5jZXMgMS1CIiwKICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gc3Rfc2ZjKHN0X3BvaW50KGxvYyksIGNycyA9IDQzMjYpKQpgYGAKClwKCk5leHQsIHdlIHVzZSB0aGUgYG9zcm1Jc29jaHJvbmUoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBpc29jaHJvbmVzLiBXZSB3aWxsIHNwZWNpZnkgdGhlICpsb2MqIHRvIGJlICpsb2Nfc2YqLCB0aGUgKmJyZWFrcyogKGluIG1pbnV0ZXMpIHdpbGwgYmUgMCB0byAzMCBtaW51dGVzIGJ5IDUgbWludXRlIGludGVydmFscywgdGhlICpyZXMqIG9yIHJlc29sdXRpb24gd2lsbCBiZSAzMCwgYW5kIHRoZSAqb3NybS5wcm9maWxlKiwgb3IgdGhlIG1vZGUsIHdpbGwgYmUgImJpa2UiLiBMZXQncyBydW4gaXQhCmBgYHtyfQppc28gPC0gb3NybUlzb2Nocm9uZShsb2MgPSBsb2Nfc2YsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAzMCwgYnkgPSA1KSwgICMgMOKAkzMwIG1pbnMgaW4gNSBtaW4gaW50ZXJ2YWxzCiAgICAgICAgICAgICAgICAgICAgIHJlcyA9IDMwLCAjIExvd2VyIHJlcyBmb3IgZmFzdGVyIHJlcXVlc3RzCiAgICAgICAgICAgICAgICAgICAgIG9zcm0ucHJvZmlsZSA9ICJiaWtlIikgICMgbW9kZSBiaWN5Y2xlCgpnbGltcHNlKGlzbykKYGBgCgpcCgpPSywgc28gbm93IHdlIGNhbiBzZWUgd2UgaGF2ZSAqaXNvbWluKiBhbmQgKmlzb21heCogYXMgb3VyIGNvbHVtbnMgZm9yIG91ciB0aW1lIGJpbnMuIEFuZCBvdXIgZGF0YXNldCBpcyBhICpNVUxUSVBPTFlHT04qLCBzbyBpdCdzIHNwYXRpYWwuIExldCdzIG1hcCB0aGlzISBGaXJzdCwgd2Ugd2lsbCBjcmVhdGUgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBjYWxsZWQgKnJ1bl90aW1lcyouIFRoZW4gd2Ugd2lsbCB1c2UgKip0bWFwKiogdG8gcGxvdCBvdXIgaXNvY2hyb25lcy4gV2Ugd2lsbCB1c2UgYSBwYWxldHRlIG9mIGhlYXQgY29sb3JzIGZvciBvdXIgaXNjaHJvbmVzLCBhbmQgd2Ugd2lsbCBhbHNvIHBsb3QgTWVkaWNhbCBTY2llbmNlcyAxLUIgaW4gdGhlcmUganVzdCBzbyB3ZSBuZXZlciBmb3JnZXQgb3VyIGJlYXV0aWZ1bCBob21lIGJhc2UuCmBgYHtyfQojIENyZWF0ZSBmYWN0b3IgY29sdW1uIGZvciB0aW1lIGludGVydmFscwppc28kcnVuX3RpbWVzIDwtIGZhY3RvcigKICBwYXN0ZShpc28kaXNvbWluLCAidG8iLCBpc28kaXNvbWF4LCAibWluIiksCiAgbGV2ZWxzID0gYygiMCB0byA1IG1pbiIsICI1IHRvIDEwIG1pbiIsICIxMCB0byAxNSBtaW4iLCAKICAgICAgICAgICAgICIxNSB0byAyMCBtaW4iLCAiMjAgdG8gMjUgbWluIiwgIjI1IHRvIDMwIG1pbiIpCikKCgojIFBsb3QgdXNpbmcgdG1hcAp0bSA8LSB0bV9zaGFwZShpc28pICsKICB0bV9wb2x5Z29ucyhjb2wgPSAicnVuX3RpbWVzIiwKICAgICAgICAgICAgICBwYWxldHRlID0gcmV2KGhlYXQuY29sb3JzKDYpKSwgICMgcmV2ZXJzZSBmb3Igc2FtZSBlZmZlY3QKICAgICAgICAgICAgICBib3JkZXIuY29sID0gImJsYWNrIiwKICAgICAgICAgICAgICBsd2QgPSAwLjUsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjMsCiAgICAgICAgICAgICAgdGl0bGUgPSAiQmlraW5nIER1cmF0aW9uIikgKwogIHRtX3NoYXBlKGxvY19zZikgKwogIHRtX2J1YmJsZXMoc2l6ZSA9IDAuMywgY29sID0gImJsdWUiKSArCiAgdG1fdGV4dCgibmFtZSIsIHNpemUgPSAxLCB5bW9kID0gLTAuNSkgCgp0bQoKYGBgCgpcCgpXb3VsZCB5b3UgbG9vayBhdCB0aGF0ISBJdCdzIGxpa2UgYSB3b3JrIG9mIGFydC4gR2VvcmdpYSBPJ0tlZWZlLCBlYXQgeW91ciBoZWFydCBvdXQuIENsaWNrIGFyb3VuZCBhbmQgZXhwbG9yZSB3aGVyZSB5b3UgY291bGQgYmlrZSB0byBpbiAxMCBtaW51dGVzIG9uY2UgY2xhc3MgZ2V0cyBvdXQhIEFuZCB5b3UndmUgZWFybmVkIHRoYXQgKipvc3JtKiogYmFkZ2UhIEdldCBiaWtpbmcgc29tZXdoZXJlLS15b3Uga25vdyB0aGUgcm91dGUgbm93IQoKXAoKIVtZb3VyIG9zcm0gQmFkZ2UhIV0ob3NybS5wbmcpLS0tCg==