如何在R中计算多边形向外偏移1单位的外包轮廓坐标
问题描述
注:我正在使用R进行开发,但本问题更多偏向逻辑思路,而非R专属问题。
我正在开发一款Shiny应用,用户可在20×20网格上绘制黑色形状,服务端中该形状以表格形式存储:表格每一行对应形状的一条边,记录边的起止点坐标,即(x, y)和(xend, yend)。
请问如何基于这张表计算得到完全包裹原形状、且和原形状保留1点间隙的外框形状的坐标表?(示例中黑色为原始形状,红色为目标外框形状)
你可以使用如下代码在本地复现上述边坐标表:
structure(list(sideNo = 1:8, x = c(4L, 4L, 8L, 8L, 4L, 8L, 15L, 18L), y = c(3L, 7L, 16L, 19L, 3L, 16L, 7L, 3L), xend = c(18L, 15L, 15L, 18L, 4L, 8L, 15L, 18L), yend = c(3L, 7L, 16L, 19L, 7L, 19L, 16L, 19L)), row.names = c(NA, -8L), class = c("tbl_df", "tbl", "data.frame"))
解决方案
你要实现的是轴对齐多边形的向外1单位偏移,属于几何处理里的缓冲区生成场景,因为你给出的示例所有边都是水平/垂直的轴对齐边,不需要处理复杂的斜线偏移,有三种成熟的实现路径:
方法1:通用几何库实现(最省事,R原生可用)
直接用空间几何库的内置缓冲区函数处理,不用自己写逻辑:
- 把边表转换成闭合多边形对象:先提取所有边的顶点,按顺序去重后组成闭合多边形
- 调用缓冲区函数向外偏移1单位,注意设置参数避免生成圆角
- 从偏移后的多边形提取边坐标,整理成和原格式一致的边表
R语言实现示例:
library(sf) library(dplyr) # 读取原始边表 edge_df <- structure(list(sideNo = 1:8, x = c(4L, 4L, 8L, 8L, 4L, 8L, 15L, 18L), y = c(3L, 7L, 16L, 19L, 3L, 16L, 7L, 3L), xend = c(18L, 15L, 15L, 18L, 4L, 8L, 15L, 18L), yend = c(3L, 7L, 16L, 19L, 7L, 19L, 16L, 19L)), row.names = c(NA, -8L), class = c("tbl_df", "tbl", "data.frame")) # 提取顶点并生成sf多边形对象 points <- rbind( edge_df %>% select(x, y) %>% rename(x = x, y = y), edge_df %>% select(xend, yend) %>% rename(x = xend, y = yend) ) %>% distinct() poly <- st_polygon(list(as.matrix(rbind(points, points[1,])))) %>% st_sfc(crs = NA) # 向外偏移1单位,设置斜接样式避免圆角 offset_poly <- st_buffer(poly, dist = 1, joinStyle = "MITRE", mitreLimit = 2) # 提取偏移后的边表 offset_coords <- st_coordinates(offset_poly)[,1:2] n <- nrow(offset_coords) offset_edge_df <- tibble( sideNo = 1:(n-1), x = offset_coords[-n, 1], y = offset_coords[-n, 2], xend = offset_coords[-1, 1], yend = offset_coords[-1, 2] )
方法2:手动实现逻辑(不需要依赖第三方库)
如果不想引入空间计算库,按下面的步骤手动处理即可:
- 区分边的类型和方向:遍历所有边,先判断是水平边(
y==yend)还是垂直边(x==xend) - 边的向外平移:
- 水平边:如果这条边是多边形的下界(边上方是形状内部),则整条边的y坐标减1;如果是上界(边下方是形状内部),则y坐标加1,同时边的x起止各向两端延伸1单位
- 垂直边:如果这条边是多边形的左界(边右方是形状内部),则整条边的x坐标减1;如果是右界(边左方是形状内部),则x坐标加1,同时边的y起止各向两端延伸1单位
- 求交拼接:相邻的平移后的边的交点就是新形状的顶点,按顺序连接所有交点,去重后就得到新的边表
方法3:网格形态学处理(适合小网格场景)
因为你的场景是20x20的小网格,用栅格处理逻辑更简单:
- 生成一个20x20的全0矩阵,把原始形状覆盖的所有网格点标记为1
- 对矩阵做一次3x3全1核的形态学膨胀操作,所有和原始形状相邻的点都会被标记为1
- 提取膨胀后区域的外轮廓,就是你要的外框形状
内容的提问来源于stack exchange,提问作者Abdallah Atef
相关产品推荐
相关产品推荐

