使用SF创建表示子组边界框的多边形

时间:2019-02-14 17:56:34

标签: r dplyr sf

我正在尝试使用sf包和管道/ tidyverse工作流程来基于另一列中定义的组生成边界框。我认为它应该像下面那样工作,但是似乎st_bbox不尊重组。

我希望收到三个多边形记录,分别代表来自a,b和c的轮廓的边界框,但我收到三个多边形记录,它们代表所有点的边界框。

library(dplyr)
library(sf)

a <- data.frame(group=rep('a',100), lon=rnorm(100,11,.2), lat=rnorm(100,53,.2))
b <- data.frame(group=rep('b',100), lon=rnorm(100,11.5,.2), lat=rnorm(100,53.5,.2))
c <- data.frame(group=rep('c',100), lon=rnorm(100,12,.2), lat=rnorm(100,54,.2))
dat <- rbind(a,b,c)

pts <- dat %>% st_as_sf(coords=c('lon','lat'),crs=4326) 

pts %>%
  group_by(group) %>%
  summarize(geometry = st_as_sfc(st_bbox(geometry)))

这将返回:

Simple feature collection with 3 features and 1 field
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 10.34313 ymin: 52.43993 xmax: 12.54254 ymax: 54.54012
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
# A tibble: 3 x 2
  group                                                                                     geometry
  <fct>                                                                                <POLYGON [°]>
1 a     ((10.34313 52.43993, 12.54254 52.43993, 12.54254 54.54012, 10.34313 54.54012, 10.34313 52...
2 b     ((10.34313 52.43993, 12.54254 52.43993, 12.54254 54.54012, 10.34313 54.54012, 10.34313 52...
3 c     ((10.34313 52.43993, 12.54254 52.43993, 12.54254 54.54012, 10.34313 54.54012, 10.34313 52...

2 个答案:

答案 0 :(得分:3)

一种选择是先使用tidyr::nest然后使用purrr::map嵌套数据框。我还使用了包装函数来简化map调用

library(tidyverse)
box_sf <- pts %>% 
  group_by(group) %>%
  nest()

bbox_wrap <- function(x) st_as_sfc(st_bbox(x))
box_sf <- box_sf %>% 
  mutate(bbox = map(data, bbox_wrap))

这将为您提供边界框列表,作为数据框的一列。如果要转换回sf对象,可以执行以下操作:

box_sf %>% 
  mutate(geometry = st_as_sfc(do.call(rbind, bbox))) %>% 
  select(-data, -bbox) %>% 
  st_as_sf()

似乎有点回旋处,我希望能按照您的原意使用group_by来解决问题

答案 1 :(得分:0)

st_bbox()函数似乎无法与group_by()一起使用,因为它从sf_points中提取了bbox属性,该属性并未为每个单独的组定义。

一种方法是使用类似以下内容的方法手动创建边界框:

library(dplyr)
library(sf)
library(ggplot2)
library(tidyr)

# function calculates angle with respect to polygon centroid.
# we need this to order the polygon correctly
calc_angle <- function(lon,lat) {
  cent_lon <- mean(lon)
  cent_lat <- mean(lat)
  ang <- atan2(lat - cent_lat, lon - cent_lon)

  return(ang)
}

bbox <-dat %>%
  group_by(group) %>%
  summarise(xmin = min(lon),ymin = min(lat), xmax=max(lon),  ymax = max(lat)) %>%
  gather(x,lon,c('xmin','xmax')) %>%
  gather(y,lat,c('ymin','ymax')) %>%
  st_as_sf(coords=c('lon','lat'),crs=4326,remove=F) %>%
  group_by(group) %>%
  mutate(angle = calc_angle(lon,lat)) %>%
  arrange(angle) %>%
  summarise(do_union=FALSE) %>%
  st_cast('POLYGON')

基本上,我们通过获取每个组的xmin,xmax,ymin,ymax来计算自己的bbox。然后,我们进行汇总以保留x和y值,并在转换为polygon之前按顺时针顺序对点进行排序。

看起来有些混乱,但这是使用group_by()解决此问题的一种方法。

pts <- dat %>% 
  st_as_sf(coords=c('lon','lat'), crs=4326, remove=F)

ggplot(pts) + geom_sf(aes(col=group)) +
  geom_sf(data=bbox, aes(col=group), fill=NA)

enter image description here