I have two cities and one individual. I'd like to find the city that is closest to this individual. To do that, I can use sf::st_nearest_feature(). However, so far it only uses the distance as the crow flies between the individual and each city. I'd like to add the constraint that the path must stay inside a polygon.
In the example below:
- individual (red triangle) is closer to city A than to city B if we consider distance as the crow flies;
- however if we add the constraint that the individual can only move inside the polygon, then it is closer to city B.
library(sf)
#> Linking to GEOS 3.9.3, GDAL 3.5.2, PROJ 8.2.1; sf_use_s2() is TRUE
library(ggplot2)
library(ggrepel)
library(rnaturalearth)
background <- ne_countries(scale = 'small', type = 'map_units', returnclass = 'sf') |>
subset(name %in% c("England", "Wales")) |>
st_union()
cities <- data.frame(
name = c("A", "B"),
lon = c(-4.3, -3.3),
lat = c(51.2, 51.45)
) |>
st_as_sf(coords = c("lon", "lat"), crs = 4326)
individual <- data.frame(id = 1, lon = -4.3, lat = 51.6) |>
st_as_sf(coords = c("lon", "lat"), crs = 4326)
ggplot() +
geom_sf(data = background) +
geom_sf(data = cities, size = 3) +
geom_sf(data = individual, color = "red", shape = 17, size = 3) +
coord_sf(xlim = c(-6, -1), ylim = c(50, 52)) +
geom_text_repel(
data = cities,
aes(geometry = geometry, label = name),
stat = "sf_coordinates",
)
#> Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
#> give correct results for longitude/latitude data

st_nearest_feature() tells me that the nearest city is A:
nearest <- st_nearest_feature(individual, cities)
cities[nearest, "name"]
#> Simple feature collection with 1 feature and 1 field
#> Geometry type: POINT
#> Dimension: XY
#> Bounding box: xmin: -4.3 ymin: 51.2 xmax: -4.3 ymax: 51.2
#> Geodetic CRS: WGS 84
#> name geometry
#> 1 A POINT (-4.3 51.2)
How can I modify my measure of distance so that the nearest city is B? If possible, the solution should scale well to do this for millions of individuals (the number of cities will stay small) in a reasonable time.
