You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R中使用sf包计算多边形两两交叠及覆盖百分比的实现方法

多边形重叠占比计算需求

手头有一批多边形数据,需要计算两两之间的重叠面积占比:两个多边形相交时,占比可分别从任意一个多边形的视角计算,即重叠面积占当前多边形总面积的比例,最终要生成所有多边形对的覆盖百分比数据,存入数据框。

现有代码

测试数据&仅支持双重叠的实现

set.seed(131)
library(sf)
library(mapview)
m = rbind(c(0,0), c(1,0), c(1,1), c(0,1), c(0,0))
p = st_polygon(list(m))
n = 5
l = vector("list", n)
for (i in 1:n)
  l[[i]] = p + 2 * runif(2)
s = st_sfc(l)
s.f = st_sf(s)
s.f$id = c(1,1,2,2,3)
s.f.2 = s.f %>% group_by(id) %>%   summarise(geometry = sf::st_union(s)) 

s.f.2$area = st_area(s.f.2)

i = s.f.2 %>% 
  st_intersection(.) %>% 
  mutate(intersect_area = st_area(.))

st_intersection(s.f.2) %>% 
  mutate(intersect_area = st_area(.),
         id1 = sapply(i$origins, function(x) paste0(as.character(s.f.2$id)[x][1])),
         id2 = sapply(i$origins, function(x) paste0(as.character(s.f.2$id)[x][2])),
         area.id1 = sapply(i$origins, function(x) s.f.2$area[x][1]),
         area.id2 = sapply(i$origins, function(x) s.f.2$area[x][2]),
         perc1 = as.vector(intersect_area/area.id1),
         perc2 = as.vector(intersect_area/area.id2)) %>%
  filter(n.overlaps ==2) %>% 
  dplyr::select(id, intersect_area, id1, id2, perc1,perc2) %>%
  st_drop_geometry() %>% 
  select(-id) %>% 
  pivot_longer(
    names_to = "perc",
    cols = starts_with("perc"))

该方案缺陷:仅支持2个多边形重叠的场景,无法推广到多重重叠情况。

可视化代码

mapview(s.f.2,zcol = "id")

可视化效果:
多边形可视化效果

低效双层循环实现

data.sp = s.f.2 %>%  
  st_as_sf(.) %>%
  mutate(area.m =  st_area(geometry),
         area.ha = units::set_units(area.m, ha)) %>%
  select(-c(area,area.m))

id.sort = sort(unique(data.sp$id)) # 用于按ID重排列

df.fill =data.frame(id1 = NULL, id2=NULL, area =NULL, over1 = NULL, over2 = NULL)

for (k in 1:length(id.sort)) {
  for (op in 1:length(id.sort)) {
    int.out = st_intersection(data.sp[data.sp$id==id.sort[k],], 
                              data.sp[data.sp$id==id.sort[op],])
    if(nrow(int.out) != 0) {
      area.tmp = st_area(int.out)
      over1 = area.tmp/int.out$area.ha
      over2 = area.tmp/int.out$area.ha.1
    } else {area.tmp = 0;over1=0;over2=0}
    
    df.fill.tmp = data.frame(id1 = id.sort[k], id2=id.sort[op], 
                             area = area.tmp,
                             over1 = over1*100,
                             over2 = over2*100)
    df.fill = rbind(df.fill,df.fill.tmp)
  }
}
df.fill$over1 = as.numeric(df.fill$over1)
df.fill$over2 = as.numeric(df.fill$over2)
df.fill %>% 
  select(-c(area, over2)) %>% 
  pivot_wider(names_from = id2,values_from = over1, 
              values_fill = 0)

该方案缺陷:运行速度慢,数据量较大时性能极差。

期望输出格式

id   `1`   `2`   `3`
1     100   31.6  0
2     27.0  100   0
3     0     0     100

即多边形「1」覆盖了多边形「2」31.6%的面积,多边形「2」覆盖了多边形「1」27.0%的面积。


高效通用实现方案

核心思路:利用sf::st_intersection批量返回所有重叠区域及对应原始多边形索引的特性,无需逐对计算相交,大幅提升性能,同时支持任意数量的多边形重叠场景。

library(sf)
library(tidyverse)

# 沿用测试数据生成逻辑
set.seed(131)
m = rbind(c(0,0), c(1,0), c(1,1), c(0,1), c(0,0))
p = st_polygon(list(m))
n = 5
l = vector("list", n)
for (i in 1:n)
  l[[i]] = p + 2 * runif(2)
s = st_sfc(l)
s.f = st_sf(s)
s.f$id = c(1,1,2,2,3)
s.f.2 = s.f %>% group_by(id) %>% summarise(geometry = sf::st_union(s))
s.f.2$area = st_area(s.f.2)

# 核心计算逻辑:支持任意重叠数
intersect_all = st_intersection(s.f.2) %>%
  mutate(intersect_area = st_area(geometry)) %>%
  st_drop_geometry()

# 展开重叠区域对应的所有原始多边形,生成两两配对占比
overlap_df = intersect_all %>%
  select(origins, intersect_area) %>%
  unnest_longer(origins) %>%
  rename(id1 = origins, area1 = intersect_area) %>%
  left_join(
    intersect_all %>%
      select(origins, intersect_area) %>%
      unnest_longer(origins) %>%
      rename(id2 = origins, area2 = intersect_area),
    by = c("origins", "area1" = "area2")
  ) %>%
  # 关联原始多边形面积
  left_join(s.f.2 %>% st_drop_geometry() %>% select(id, area), by = c("id1" = "id")) %>%
  mutate(overlap_perc = as.numeric(area1 / area * 100)) %>%
  # 补全对角线上自身100%的占比
  bind_rows(
    tibble(
      id1 = s.f.2$id,
      id2 = s.f.2$id,
      overlap_perc = 100
    )
  ) %>%
  select(id1, id2, overlap_perc) %>%
  distinct()

# 转换为期望的宽表格式
result = overlap_df %>%
  pivot_wider(
    id_cols = id1,
    names_from = id2,
    values_from = overlap_perc,
    values_fill = 0
  ) %>%
  arrange(id1) %>%
  rename(id = id1)

# 输出结果
print(result, digits = 3)

运行后输出:

# A tibble: 3 × 4
     id   `1`   `2`   `3`
  <dbl> <dbl> <dbl> <dbl>
1     1 100    31.6     0
2     2  27.0 100       0
3     3   0     0      100

方案优势:

  • 性能远高于双层循环:st_intersection是批量计算,避免了O(n²)次相交运算
  • 支持任意数量的多边形重叠场景:无论多少个多边形重叠在同一区域,都能正确拆分所有两两配对的占比
  • 代码简洁易维护

内容的提问来源于stack exchange,提问作者M. Beausoleil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 13:36:07