R语言 如何重新排序x,y坐标点以形成闭合环

sg24os4d  于 2023-01-15  发布在  其他
关注(0)|答案(3)|浏览(173)

我有一组点,它们勾勒出一个Map区域的轮廓。我想使用这些点定义一个多边形,以便使用简单要素(sf)库使用ggplot绘制该多边形。但是,这些点的顺序不对,因此连接它们的线不能形成一个闭合形状。我已经尝试了geom_line和geom_path。是否有任何方法可以重新组织这些点,而无需手动排序?
在下面这个可重复的例子中,“好圆”是我想要的,而“坏圆”是我拥有的,它们都包括相同的x,y点,但由于坏圆的顺序不对,我无法画出一个封闭的图形。

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

x_coords <- rad * cos(seq(0, (2 * pi), (pi / 6)))
y_coords <- rad * sin(seq(0, (2 * pi), (pi / 6)))
perimeter <- as.matrix(rbind(cbind(x_coords, y_coords), c(x_coords[1], y_coords[1])))
perimeter

good_circle = st_multipolygon(list(list(perimeter)))
ggplot() + geom_sf(data = good_circle, fill = "blue") + ggtitle("good circle")

我得到的结果是这样的:

bad_circle <- slice(data.frame(perimeter), sample(1:n()))
ggplot(bad_circle, aes(x = x_coords, y = y_coords)) + geom_point() + geom_line() + ggtitle("bad circle")

eh57zj3b

eh57zj3b1#

我建议你用一种不同的方法,不用重新排序点,你可以在你的无序点周围创建一个convex hull并得到多边形。
虽然sf有一个函数(sf::st_convex_hull()),但我通常更喜欢concaveman包的结果,它也与sf对象兼容。

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

rad <- 1

x_coords <- rad * cos(seq(0, (2 * pi), (pi / 6)))
y_coords <- rad * sin(seq(0, (2 * pi), (pi / 6)))
perimeter <- as.matrix(rbind(cbind(x_coords, y_coords), c(x_coords[1], y_coords[1])))
perimeter
#>            x_coords      y_coords
#>  [1,]  1.000000e+00  0.000000e+00
#>  [2,]  8.660254e-01  5.000000e-01
#>  [3,]  5.000000e-01  8.660254e-01
#>  [4,]  6.123032e-17  1.000000e+00
#>  [5,] -5.000000e-01  8.660254e-01
#>  [6,] -8.660254e-01  5.000000e-01
#>  [7,] -1.000000e+00  1.224606e-16
#>  [8,] -8.660254e-01 -5.000000e-01
#>  [9,] -5.000000e-01 -8.660254e-01
#> [10,] -1.836910e-16 -1.000000e+00
#> [11,]  5.000000e-01 -8.660254e-01
#> [12,]  8.660254e-01 -5.000000e-01
#> [13,]  1.000000e+00 -2.449213e-16
#> [14,]  1.000000e+00  0.000000e+00

bad_circle <- slice(data.frame(perimeter), sample(1:n()))

# sf + concaveman

poly <- bad_circle %>%
  # to sf
  st_as_sf(coords = c("x_coords", "y_coords")) %>%
  # w/ concaveman
  concaveman::concaveman()

# A polygon
poly
#> Simple feature collection with 1 feature and 0 fields
#> Geometry type: POLYGON
#> Dimension:     XY
#> Bounding box:  xmin: -1 ymin: -1 xmax: 1 ymax: 1
#> CRS:           NA
#>                         polygons
#> 1 POLYGON ((-0.866 -0.5, -1 1...

# Plot

ggplot() +
  geom_sf(data = poly, fill = "blue") +
  ggtitle("poly w/ concaveman")

创建于2023年1月13日,使用reprex v2.0.2
https://en.wikipedia.org/wiki/Convex_hull

qc6wkl3g

qc6wkl3g2#

将点转换为极坐标:

polar = data.frame(r = sqrt(bad_circle$x_coords^2 + bad_circle$y_coords^2), theta = atan2(bad_circle$y_coords, bad_circle$x_coords))

然后按Angular 的顺序画出这些点:

plot(bad_circle[order(polar$theta),], type="l")

要保持ggplot中的顺序,请使用geom_path:

ggplot(bad_circle[order(polar$theta),], aes(x = x_coords, y = y_coords)) + geom_point() + geom_path()
voase2hg

voase2hg3#

您描述的操作可以解释为查找convex hull
这并不是一个不常见的操作,因此在{sf}环境中有标准化的工具来实现它(尽管我必须说,将点转换为极坐标,然后按照@user1505631的建议找到r和theta是相当有创造性的)。
我的建议是,首先通过sf::st_union()调用将多个单独的点转换为单个多点对象,然后通过sf::st_convex_hull()调用找到它的船体。
这是可能的-特别是在点的情况下,不是那么方便的间隔作为您的圆-船体将不完全符合期望;在这种情况下,还考虑concaveman::concaveman();对于实际应用中的示例(以及真实的世界数据中凸面 shell 和凹面 shell 之间的差异),请查看How to draw Polygon for lots of lat/long coordinates and calculate surface are?

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

rad <- 5

x_coords <- rad * cos(seq(0, (2 * pi), (pi / 6)))
y_coords <- rad * sin(seq(0, (2 * pi), (pi / 6)))
perimeter <- as.matrix(rbind(cbind(x_coords, y_coords), c(x_coords[1], y_coords[1])))

bad_circle <- slice(data.frame(perimeter), sample(1:n())) %>% 
  st_as_sf(coords = c("x_coords", "y_coords"))

hull <- bad_circle %>% 
  st_union() %>% 
  st_convex_hull()

plot(hull, col = "gray")
plot(bad_circle, col = "red",  pch = 4, add = T)

相关问题