多边形内的 r sf 包质心
r sf package centroid within polygon
我需要给多边形添加标签,我通常使用质心,但质心不在多边形内。我发现了这个问题 但我使用的是 sf 包。
下面是玩具数据
rm(list = ls(all = TRUE)) #start with empty workspace
library(sf)
library(tidyverse)
library(ggrepel)
pol <- st_polygon(list(rbind(c(144, 655),c(115, 666)
,c(97, 660),c(86, 640)
,c(83, 610),c(97, 583)
,c(154, 578),c(140, 560)
,c(72, 566),c(59, 600)
,c(65, 634),c(86, 678)
,c(145, 678),c(144, 655)))) %>%
st_sfc()
a = data.frame(NAME = "A")
st_geometry(a) = pol
a <- a %>%
mutate(lon = map_dbl(geometry, ~st_centroid(.x)[[1]]),
lat = map_dbl(geometry, ~st_centroid(.x)[[2]]))
ggplot() +
geom_sf(data = a, fill = "orange") +
geom_label_repel(data = a, aes(x = lon, y = lat, label = NAME))
结果如下
简单的答案是将 st_centroid
替换为 st_point_on_surface
。在质心位于多边形内部的情况下,这不会 return 真正的质心。
a2 <- a %>%
mutate(lon = map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]]))
ggplot() +
ggplot2::geom_sf(data = a2, fill = "orange") +
geom_label_repel(data = a2, aes(x = lon, y = lat, label = NAME))
或者
如果多边形的质心位于多边形内部,则使用该质心,否则,在多边形内找到一个点。
st_centroid_within_poly <- function (poly) {
# check if centroid is in polygon
centroid <- poly %>% st_centroid()
in_poly <- st_within(centroid, poly, sparse = F)[[1]]
# if it is, return that centroid
if (in_poly) return(centroid)
# if not, calculate a point on the surface and return that
centroid_in_poly <- st_point_on_surface(poly)
return(centroid_in_poly)
}
a3 <- a %>%
mutate(lon = map_dbl(geometry, ~st_centroid_within_poly(.x)[[1]]),
lat = map_dbl(geometry, ~st_centroid_within_poly(.x)[[2]]))
ggplot() +
ggplot2::geom_sf(data = a3, fill = "orange") +
geom_label_repel(data = a3, aes(x = lon, y = lat, label = NAME))
上面st_centroid_within_polygon
的函数改编自 for the sf
package. A more thorough review of how st_point_on_surface
works can be found here。
扩展 Mitch 的回答,因为上面提供的 st_centroid_within_poly
函数仅适用于单个多边形。
要在多个多边形上使用,请使用:
st_centroid_within_poly <- function (poly) {
# check if centroid is in polygon
ctrd <- st_centroid(poly, of_largest_polygon = TRUE)
in_poly <- diag(st_within(ctrd, poly, sparse = F))
# replace geometries that are not within polygon with st_point_on_surface()
st_geometry(ctrd[!in_poly,]) <- st_geometry(st_point_on_surface(poly[!in_poly,]))
ctrd
}
我需要给多边形添加标签,我通常使用质心,但质心不在多边形内。我发现了这个问题
下面是玩具数据
rm(list = ls(all = TRUE)) #start with empty workspace
library(sf)
library(tidyverse)
library(ggrepel)
pol <- st_polygon(list(rbind(c(144, 655),c(115, 666)
,c(97, 660),c(86, 640)
,c(83, 610),c(97, 583)
,c(154, 578),c(140, 560)
,c(72, 566),c(59, 600)
,c(65, 634),c(86, 678)
,c(145, 678),c(144, 655)))) %>%
st_sfc()
a = data.frame(NAME = "A")
st_geometry(a) = pol
a <- a %>%
mutate(lon = map_dbl(geometry, ~st_centroid(.x)[[1]]),
lat = map_dbl(geometry, ~st_centroid(.x)[[2]]))
ggplot() +
geom_sf(data = a, fill = "orange") +
geom_label_repel(data = a, aes(x = lon, y = lat, label = NAME))
结果如下
简单的答案是将 st_centroid
替换为 st_point_on_surface
。在质心位于多边形内部的情况下,这不会 return 真正的质心。
a2 <- a %>%
mutate(lon = map_dbl(geometry, ~st_point_on_surface(.x)[[1]]),
lat = map_dbl(geometry, ~st_point_on_surface(.x)[[2]]))
ggplot() +
ggplot2::geom_sf(data = a2, fill = "orange") +
geom_label_repel(data = a2, aes(x = lon, y = lat, label = NAME))
或者
如果多边形的质心位于多边形内部,则使用该质心,否则,在多边形内找到一个点。
st_centroid_within_poly <- function (poly) {
# check if centroid is in polygon
centroid <- poly %>% st_centroid()
in_poly <- st_within(centroid, poly, sparse = F)[[1]]
# if it is, return that centroid
if (in_poly) return(centroid)
# if not, calculate a point on the surface and return that
centroid_in_poly <- st_point_on_surface(poly)
return(centroid_in_poly)
}
a3 <- a %>%
mutate(lon = map_dbl(geometry, ~st_centroid_within_poly(.x)[[1]]),
lat = map_dbl(geometry, ~st_centroid_within_poly(.x)[[2]]))
ggplot() +
ggplot2::geom_sf(data = a3, fill = "orange") +
geom_label_repel(data = a3, aes(x = lon, y = lat, label = NAME))
上面st_centroid_within_polygon
的函数改编自sf
package. A more thorough review of how st_point_on_surface
works can be found here。
扩展 Mitch 的回答,因为上面提供的 st_centroid_within_poly
函数仅适用于单个多边形。
要在多个多边形上使用,请使用:
st_centroid_within_poly <- function (poly) {
# check if centroid is in polygon
ctrd <- st_centroid(poly, of_largest_polygon = TRUE)
in_poly <- diag(st_within(ctrd, poly, sparse = F))
# replace geometries that are not within polygon with st_point_on_surface()
st_geometry(ctrd[!in_poly,]) <- st_geometry(st_point_on_surface(poly[!in_poly,]))
ctrd
}