简体   繁体   中英

How to filter sfc polygons by long/lat values?

Suppose t is:

t <- structure(list(structure(list(structure(c(-89.990791, -89.990772, 
-89.990901, -89.99092, -89.990791, 30.727025, 30.727083, 30.727114, 
30.727057, 30.727025), .Dim = c(5L, 2L))), class = c("XY", "POLYGON", 
"sfg")), structure(list(structure(c(-89.991691, -89.991755, -89.991755, 
-89.991691, -89.991691, 30.716004, 30.716004, 30.715916, 30.715916, 
30.716004), .Dim = c(5L, 2L))), class = c("XY", "POLYGON", "sfg"
))), class = c("sfc_POLYGON", "sfc"), precision = 0, bbox = structure(c(xmin = -89.991755, 
ymin = 30.715916, xmax = -89.990772, ymax = 30.727114), class = "bbox"), crs = structure(list(
    epsg = 4326L, proj4string = "+proj=longlat +datum=WGS84 +no_defs"), class = "crs"), n_empty = 0L)
> t
Geometry set for 2 features 
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -89.99176 ymin: 30.71592 xmax: -89.99077 ymax: 30.72711
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
POLYGON ((-89.99079 30.72703, -89.99077 30.7270...
POLYGON ((-89.99169 30.716, -89.99175 30.716, -...

How can I filter polygons by long/lat boundaries? Suppose I want to filter out any polygon that hax lat>30.72 (so to keep only the second polygon). Is there any specific function I can use to filter polygons?

I do not know if there is a ready-made function, but a rectangular “spatial filter” is easy to build. Just define the “corners”, create a bbox from them, convert to polygon and find which of your original polygons are contained / overlap the "filter area".

Here is a quick-and-dirty example:

library(sf)
polys_sf<-st_read(system.file("shape/nc.shp", package="sf") ) %>% 
  st_transform(crs="+init=epsg:4326")
plot(st_geometry(polys_sf))

Define a “spatial filter”

xmin <- -80
xmax <- -76
ymin <- 34
ymax <- 36

create a polygon based on the filter. (You can use "NA" for some values, so if you want for example to filter only "on the left", you can set xmax to NA)

filt_bbox <- sf::st_bbox(c(xmin = ifelse(is.na(xmin), -180, xmin), 
                           ymin = ifelse(is.na(ymin),  -90,  ymin), 
                           xmax = ifelse(is.na(xmax), +180, xmax), 
                           ymax = ifelse(is.na(ymax),  +90, ymax)), 
                         crs = st_crs(4326)) %>% 
  sf::st_as_sfc(.)

Now "filter" the original dataset based on the bbox polygon: Use st_within if you want to keep only the polys completely contained in the defined area

find_data <- sf::st_within(polys_sf, filt_bbox)
#> although coordinates are longitude/latitude, st_within assumes that they are planar
filt_data <- polys_sf[which(lengths(find_data) != 0), ]

plot(filt_bbox)
plot(st_geometry(filt_data), add = TRUE, reset = FALSE)

Use st_intersects if you want to keep all polys that intersect the defined area

find_data <- sf::st_intersects(polys_sf, filt_bbox)
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar
filt_data <- polys_sf[which(lengths(find_data) != 0), ]

plot(st_geometry(filt_data))
plot(filt_bbox, add = TRUE)

(Clearly, this works if both your polys and the “filtering extent” are lat/long, otherwise you have to take care of reprojecting, etcetera.) Created on 2018-11-15 by the reprex package (v0.2.1)

You can do this a lot easier using a simple function:

filter_sf <- function(.data, xmin = NULL, xmax = NULL, ymin = NULL, ymax = NULL) {
  bb <- sf::st_bbox(.data)
  if (!is.null(xmin)) bb["xmin"] <- xmin
  if (!is.null(xmax)) bb["xmax"] <- xmax
  if (!is.null(ymin)) bb["ymin"] <- ymin
  if (!is.null(ymax)) bb["ymax"] <- ymax
  sf::st_filter(.data, sf::st_as_sfc(bb), .predicate = sf::st_within)
}

So now you can do:

t %>%
  filter_sf(ymin > 30.72)

This method works for both POLYGON and MULTIPOLYGON geometries.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM