0

I was wondering if it was possible to do nearest neighbor analysis using road distance in R? I've seen some attempts to do it with QGIS (Calculating the nearest neighbour using given road network in QGIS), but don't know if an R functionality is available.

For example, if I am looking at nursing homes nationwide and trying to identify the five closest hospitals to each one. I had used Euclidean distance before using st_nn but I am now interested in trying to replicate the analysis using road distance. I know that if I had any two points I could use a number of packages to define road distance between them (e.g. googleway), but not sure if that is available for k-nearest neighbor approaches.

0

2 Answers 2

1

I would probably use dodgr alongside sf for that. As an meta-example, consider a road network from OSM labelled "osm_file", where we filter the most important roads:

roads <- read_sf("osm_file") %>% 
  filter(fclass %in% c("motorway", "motorway_link", "trunk", "trunk_link", 
                       "primary", "primary_link", "secondary", "secondary_link",
                       "tertiary", "tertiary_link")) %>% 
  st_transform(crs = "WGS84") %>% 
  mutate(id = row_number())

We can now create street networks with dodgr, and in this example select the largest enclosed network:

# Create road network graph
graph <- weight_streetnet(roads, wt_profile = "motorcar", type_col = "fclass")
graph_simple <- graph [graph$component == 1, ]
vertices <- dodgr_vertices(graph_simple)

It is important to investigate how many components are in the graph. Then, we define coordinates of your from and to points. In this case, home and hospital would be sf POINTS objects:

# Define from coordinates
from_x <- st_coordinates(home)[,"X"]
from_y <- st_coordinates(home)[,"Y"]

# Define to coordinates
to_x <- st_coordinates(hospital)[,"X"]
to_y <- st_coordinates(hospital)[,"Y"]

And then finally calculate the distances along the road network:

distances <- dodgr_dists (graph = graph_simple, 
                          from = cbind (from_x, from_y), 
                          to = cbind (to_x, to_y))

Then you can filter the distances and find the five closest hospitals for each home. It is important to note that you can get really long distances if your selected component does not provide good coverage of your input points. Alternatively, you could loop over each input point and only select the roads which are within a certain buffer (let's say you are not interested in hospitals more than 100 km away for example). You do not have to worry about your from/to points not being on the roads themselves, since dodgr is smart:

For spatial graphs—that is, those containing columns of latitudes and longitudes (or “x” and “y”)—routing points can be represented by a simple matrix of arbitrary latitudes and longitudes (or, again, “x” and “y”). dodgr_distances() will map these points to the closest network points, and return corresponding shortest-path distances.

In addition, you can run the calculations in parallel, making it extremely fast. I suggest you have a look at some examples on their page.

0

If would be good if you clarified, distance along the network (roads) or across the entire spatial domain in relation to the network? There are some network tools available in one of the spatstat packages spatstat.linnet (on CRAN). Not knowing your data I can only speculate but, spatstat.linnet::distfun.lpp may be what your are after. This function supports distance analysis of a point pattern (events) along a network. For calculating distance or nearest neighbors between two different point patterns (ie., nursing homes and hospitals) you can use the bivariate version spatstat.linnet::crossdist to return a distance matrix or spatstat.linnet::nndist for k nearest neighbors. I believe that there is a method available for lpp class objects with the nndist function but if not, you will have to query the distance matrix, returned from crossdist, for the desired knn.

2
  • I'm not certain I completely comprehend the distinction being made, so I clarified my original post a bit. Thanks.
    – josephn
    Feb 6, 2023 at 21:31
  • The distfun function, with the k=5 argument, I indicated would work for your nursing home question. You "network" would be roads with nursing homes and hospitals the "events". You will have to read up on the package to understand how to structure your data. For your point pattern, you will have to structure it as a marked (hospitals, nursing homes) ppp object. There are very good vignettes associated with each spatstat library. Feb 6, 2023 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.