3

I am doing a point in polygon analysis

library(terra)
library(rnaturalearth)
  
crdref <- "+proj=longlat +datum=WGS84"

lonlat<- structure(c(-123.115684, -81.391114, -74.026122, -122.629252, 
                      -159.34901, 7.76101, 48.080979, 31.159987, 40.621058, 47.50331, 
                       21.978049, 36.90086), .Dim = c(6L, 2L), 
                      .Dimnames = list(NULL,c("longitude", "latitude")))

pts <- vect(lonlat, crs = crdref)
world_shp <- rnaturalearth::ne_countries()
world_shp <- terra::vect(world_shp, crs = crdref)
world_shp <- terra::project(world_shp, crdref)

plot(world_shp)
points(pts, col = "red", pch = 20)      

enter image description here

All these points lie on the edges of polygon and hence when I try to extract the the polygon under which each point lies, I get an NA

e <- terra::extract(world_shp, pts) 
e$sovereignt
NA
  

Is there any way I can return the nearest polygon for each point using the terra package

89_Simple
  • 3,393
  • 3
  • 39
  • 94
  • I'm getting an error on `> pts <- vect(lonlat, crs = crdref) Error in h(simpleError(msg, call)) : error in evaluating the argument 'x' in selecting a method for function 'vect': object 'lonlat' not found` – Mossa Jun 09 '22 at 14:27
  • Apologies. I have edited the mistake in the question – 89_Simple Jun 09 '22 at 15:08

2 Answers2

1

You can use st_join from the sf package:

library(rnaturalearth)
library(tidyverse)
library(sf)
library(rgdal)

devtools::install_github("ropensci/rnaturalearthhires")
library(rnaturalearthhires)


# create points dataframe
points <- tibble(longitude = c(123.115684, -81.391114, -74.026122, -122.629252,
                               -159.34901, 7.76101),
                 latitude = c(48.080979, 31.159987, 40.621058, 47.50331,
                              21.978049, 36.90086)) %>% 
  mutate(id = row_number())

# convert to spatial
points <- points %>% 
  st_as_sf(coords = c("longitude", "latitude"),
           crs = 4326)

# load Natural Earth data
world_shp <- rnaturalearth::ne_countries(returnclass = "sf",
                                         scale = "large")


# Plot points on top of map (just for fun)
ggplot() +
  geom_sf(data = world_shp) +
  geom_sf(data = points, color = "red", size = 2)


# check that the two objects have the same CRS
st_crs(points) == st_crs(world_shp)


# locate for each point the nearest polygon
nearest_polygon <- st_join(points, world_shp,
                           join = st_nearest_feature)

nearest_polygon
NorthNW
  • 157
  • 6
  • Please try to plot the result of `world_shp` after `st_make_valid`. – Mossa Jun 09 '22 at 14:48
  • Dear lord. Those polygons were definitely very invalid – NorthNW Jun 09 '22 at 14:56
  • I have edited my answer in response to @Mossa's comment. It was an error with the `ne_countries()` function which can be fixed by specifying `returnclass` and `scale`. This, in turn, requires the `rnaturalearthhires` package to be installed – NorthNW Jun 09 '22 at 15:07
  • okay thanks. I have done similar solution earlier but wanted to check if there's anything in the `terra` package that can be used. But thanks. I will accept the answer after a while – 89_Simple Jun 09 '22 at 15:11
  • 2
    The `terra` package is mostly for raster data. You tried to treat the polygons as raster data (with `extract`). I see no reason why you wouldn't use an `sf` approach – NorthNW Jun 09 '22 at 15:16
  • 1
    I love this, upvoted answer; Thanks a lot! – Mossa Jun 09 '22 at 20:35
0

You can use terra::nearest

Example data

library(terra)
v <- vect(system.file("ex/lux.shp", package="terra"))
pts <- matrix(c(5.812,6.577,5.864,50.126,49.774,49.488), ncol=2)
pts <- vect(pts, crs = crs(w))

Solution

pv = as.points(v)
n = nearest(pts, pv)

i = pv$NAME_2[n$to_id]
i
#[1] "Clervaux"         "Echternach"       "Esch-sur-Alzette"

Illustration

plot(v, border="gray", lwd=3)
text(v, "NAME_2", cex=.6)
points(pts)
lines(pts, n, col="blue", lwd=3)
text(pts, i, cex=.6, col="red", halo=TRUE, adj=c(.5,1.5))

enter image description here

For the example above, you could also do

n = nearest(pts, v)

But in most cases you would want the distance to the border, not the centroids and do

n = nearest(pts, v, centroids=FALSE)

The current version returns the wrong IDs (for the nodes, not the polygons) so that cannot be used

Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63