R Proximity – Finding ‘n’ Closest Points to Each Point in SpatialPointsDataFrame

pointproximityrrastersf

I am working with a dataset that is similar to this one:

library(raster)
library(sf)

# Define CRSs
crs.latlon <- "+init=epsg:4326"
crs.sirgas <- "+init=epsg:5880"

# define edges in somewhere within brazil
(spbb <- matrix(c(-53,-44,-25,-20), nrow=2, byrow=F))

# create spatial points in lat/lon
spbb <- SpatialPoints(spbb, CRS(crs.latlon))

# convert to sirgas 2000
(spbb <- spTransform(spbb, CRS(crs.sirgas)))

# make a raster extent object rounded to km:
ext <- extent(5200000, 6046000, 7234000, 7756000)

# create raster
r <- raster(ext, ncol=252, nrow=156, crs=crs.sirgas) # ~3 km resolution
r[] <- runif(ncell(r)) * 10

# create station points
pts <- sampleRandom(r, 500, na.rm=TRUE, sp=TRUE, cells=T) #%>%
#plot(st_geometry(sf::st_as_sfc(pts)), col=sf.colors(4), axes = TRUE, cex=2, pch=19)

#check it
plot(r)
plot(pts, add=T, col="red", pch=19)

For each point in pts, I would like to find the n (i.e. 5) closest surrounding ones. The resulting data frame would be something roughly similar to:

id      nearest     distance
pt1       pt3         14607
pt1       pt204       8540
pt1       pt301       6306
pt1       pt455       4956
pt1       pt337       2145

And so on for each point in the dataset.

What is the fastest way to achieve that?

Best Answer

The geos functions provided in sf, combined with some matrix operations, will do the trick. First, convert and transform your points.

library(sf)

spbb                             # define your points here

spbb <- st_as_sf(spbb)           # convert to simple features
spbb <- st_transform(spbb, 5880) # transform to your desired proj (unit = m)

Then, use st_distance to create a matrix of distances between each point in the data frame. Replace the diagonal with NAs (because distance of a point to itself is 0).

dist_matrix   <- st_distance(spbb, spbb)           # creates a matrix of distances
diag(dist_matrix) <- NA                            # replaces 0s with NA

Lastly, use some matrix operations to get the smallest value of each row.

spbb$distance <- rowMins(dist_matrix)              # get the dist of nearest element
spbb$nearest  <- rowMins(dist_matrix, value = T)   # get the index of the nearest element
Related Question