I would like to calculate a distance matrix between the centroids of the polygons in a region considering spatial link distance (aka 'neighbor distances') instead of simple Euclidean distances. Spatial link distance considers the Euclidean distances along the links of spatial neighbor links.
In other words, I would like to calculate spatial distance link matrix. Is there a package / function to do this?
I've done a quick exploration with sfdep and here's the solution I've found in the repex below:
reprex
Step 1: calculate distances between neighboring polygons (dists
) using sfdep::st_nb_dists()
:
library(sf)
library(sfdep)
library(data.table)
library(fields)
# get contiguity and distance btwn neighbors
geo <- sf::st_geometry(guerry)
nb <- sfdep::st_contiguity(geo)
dists <- sfdep::st_nb_dists(geo, nb)
Step 2: I've created a nb_list_to_df()
function to convert dists
into a data.frame
in long format with the distance for every pair or neighboring polygons (od_df
)
# fun to convert nb dist to a data.frame in long format
nb_list_to_df <- function(nb, dists) {
mtrx <- sfdep::wt_as_matrix(nb, dists)
matrix_length <- 1:length(mtrx[1,])
# FULL MATRIX
mtrx_long <- cbind(
as.data.table(
data.table::CJ(matrix_length, matrix_length)), # df two columns
'dist' = as.vector(mtrx) # matrix values in a vector
)
# keep only dist between neighbors
mtrx_long <- subset(mtrx_long, dist >0)
setnames(mtrx_long, c('from', 'to', 'dist'))
return(mtrx_long)
}
# convert nb dist to a data.frame in long format
od_df <- nb_list_to_df(nb, dists)
head(od_df)
#> orig dest dist
#> 1: 1 36 90030.63
#> 2: 1 37 87399.28
#> 3: 1 67 55587.69
#> 4: 1 69 85693.43
#> 5: 2 7 85221.38
#> 6: 2 49 75937.49
The final steps are to:
- Use
od_df
to create a network graph using a library likeigraph
. In this case, I'm usingcppRouting
because it seems to be the most efficient. - Use the network topology to calculate the distance between all combinations of origin-destination pairs
# step 3: create a network graph
library(cppRouting)
graph <- makegraph(od_df, directed = F)
# step 4: get the distances considering network topology
dist_link <- get_distance_matrix(Graph=graph,
from = unique(od_df$orig),
to = unique(od_df$orig))
This code arrives at the desired solution. However, I'm wondering if this is a good approach or if there are better / more efficient ways to do this.