This vignette builds on the transport chapter of the Geocomputation with R book by showing how to create multi-stage desire lines, from the ground-up.
It depends on these packages and datasets:
library(sf)
library(stplanr)
library(dplyr)
library(spDataLarge)
desire_lines = od2line(bristol_od, bristol_zones)
desire_rail = top_n(desire_lines, n = 3, wt = train)
The first stage is to create matrices of coordinates that will subsequently be used to create matrices representing each leg:
mat_orig = as.matrix(line2df(desire_rail)[c("fx", "fy")])
mat_dest = as.matrix(line2df(desire_rail)[c("tx", "ty")])
mat_rail = st_coordinates(bristol_stations)
The outputs are three matrices representing the starting points of
the trips, their destinations and possible intermediary points at public
transport nodes (named orig
, dest
and
rail
respectively). But how to identify which
intermediary points to use for each desire line? The knn()
function from the nabor package (which is used
internally by stplanr so it should already be
installed) solves this problem by finding k nearest neighbors
between two sets of coordinates. By setting the k
parameter, one can define how many nearest neighbors should be returned.
Of course, k
cannot exceed the number of observations in
the input (here: mat_rail
). We are interested in just one
nearest neighbor, namely, the closest railway station:
knn_orig = nabor::knn(mat_rail, query = mat_orig, k = 1)$nn.idx
knn_dest = nabor::knn(mat_rail, query = mat_dest, k = 1)$nn.idx
This results not in matrices of coordinates, but row indices that can
subsequently be used to subset the mat_rail
. It is worth
taking a look at the results to ensure that the process has worked
properly, and to explain what has happened:
## [1] 33 11 39
## [1] 7 7 7
The output demonstrates that each object contains three whole numbers
(the number of rows in desire_rail
) representing the rail
station closest to the origin and destination of each desire line. Note
that while each ‘origin station’ is different, the destination (station
30
) is the same for all desire lines. This is to be
expected because rail travel in cities tends to converge on a single
large station (in this case Bristol Temple Meads). The indices can now
be used to create matrices representing the rail station of origin and
destination:
The final stage is to convert these matrices into meaningful geographic objects, in this case simple feature ‘multilinestrings’ that capture the fact that each stage is a separate line, but part of the same overall trip:
mats2line = function(mat1, mat2) {
lapply(1:nrow(mat1), function(i) {
rbind(mat1[i, ], mat2[i, ]) %>%
st_linestring()
}) %>% st_sfc()
}
desire_rail$leg_orig = mats2line(mat_orig, mat_rail_o)
desire_rail$leg_rail = mats2line(mat_rail_o, mat_rail_d)
desire_rail$leg_dest = mats2line(mat_rail_d, mat_dest)
The results are visualised below: