---
title: "Routing and isochrones"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Routing and isochrones}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 6.5, fig.height = 5.5)
```

```{r setup}
library(osmnxr)
g <- ox_example("olinda")
```

Routing in `osmnxr` runs in the Rust core (Dijkstra, Yen, multi-source). We use
the bundled real network of central Olinda, Brazil, so this runs offline.

## Shortest path by distance

Snap two coordinates to the nearest nodes, then route between them:

```{r}
orig <- ox_nearest_nodes(g, x = -34.8553, y = -8.0089)
dest <- ox_nearest_nodes(g, x = -34.8505, y = -8.0125)
route <- ox_shortest_path(g, orig, dest, weight = "length")
length(route) # nodes along the route
```

```{r}
route_xy <- sf::st_coordinates(g$nodes)[match(route, g$nodes$osmid), ]
plot(g, col = "grey80", lwd = 0.6)
lines(route_xy, col = "#b7410e", lwd = 3)
points(route_xy[c(1, nrow(route_xy)), ], pch = 19, col = "#0d3b66", cex = 1.2)
```

## Travel time instead of distance

Real routing usually minimises *time*, not distance. Impute free-flow speeds
from each road's class, derive per-edge travel times, then route on them
(Boeing 2025, `routing` module):

```{r}
g <- ox_add_edge_travel_times(g)
head(g$edges[c("highway", "length", "speed_kph", "travel_time")])

route_t <- ox_shortest_path(g, orig, dest, weight = "travel_time")
identical(route_t, route) # may differ: the fastest route is not always shortest
```

## Route alternatives

`ox_k_shortest_paths()` returns the *k* shortest loopless routes (Yen's
algorithm) — useful for comparing options or modelling redundancy:

```{r}
ox_k_shortest_paths(g, orig, dest, k = 3, weight = "travel_time")
```

## Isochrones (service areas)

An isochrone is the area reachable from an origin within a budget. With
`travel_time` as the weight, cutoffs are in seconds — here, 1- and 2-minute
drive-time service areas from a central point:

```{r}
centre <- ox_nearest_nodes(g, x = -34.8553, y = -8.0089)
iso <- ox_isochrone(g, centre, cutoffs = c(60, 120), weight = "travel_time")
iso[c("cutoff", "n_nodes")]
```

```{r}
plot(g, col = "grey85", lwd = 0.6)
plot(sf::st_geometry(iso), add = TRUE, border = NA,
     col = grDevices::adjustcolor(c("#0d3b66", "#2a9d8f"), 0.4))
```

## Many-to-many distances

For accessibility work you often need a full origin–destination matrix in one
call (see the [Accessibility](accessibility.html) article):

```{r}
hubs <- ox_nearest_nodes(g,
  x = c(-34.8553, -34.8505, -34.852),
  y = c(-8.0089, -8.0125, -8.006))
round(ox_distance_matrix(g, hubs, hubs, weight = "travel_time"))
```

## References

Boeing, G. (2025). Modeling and analyzing urban networks and amenities with
OSMnx. *Geographical Analysis*.
