创建一个已知区域的圆圈
Creating a Circle of Known Area
我想创建一个面积为 100 的圆作为 sf 对象。我以为 st_buffer() 会做,但面积是
略小于 100.
pt.df <- data.frame(pt = 1, x = 20, y = 20)
pt.sf <- st_as_sf(pt.df, coords = c("x", "y"))
circle1 <- st_buffer(pt.sf, dist = sqrt(100 / pi))
st_area(circle1) # 99.95431 on my PC
我可以用软糖因子乘以半径,我得到了我想要的。
fudge <- sqrt( 100 / st_area(circle1) )
circle2 <- st_buffer(pt.sf, dist = fudge * sqrt(100 / pi))
st_area(circle2) # 100
但使用软糖因素似乎很愚蠢。
有没有办法在 sf 包内创建一个已知区域的圆而不用
st_buffer 中的软糖因素 ?
用圆周率的截断形式计算半径是一个浮点精度问题。这将导致一个圆圈略小于您想要的输出。可以看到pi只能存储到机器精度:
.Machine$double.eps
# 2.22044604925031e-16
pi
# 3.14159265358979
如果你想修正它,你可以在你想要的区域使用线性修正。请注意,这仍然是一个近似值,但它应该让您更接近您想要的结果。
radius <- function(area){
A <- area + (area * 0.000457099999999997)
return(sqrt(A / pi))
}
system.file("shape/nc.shp", package="sf") %>%
st_read() %>%
st_centroid() %>%
st_transform(st_crs(5070)) %>%
st_buffer(radius(100)) %>%
st_area()
主要问题是 st_buffer
在内部使用多边形,而不是圆形。增加 nQuadSegs
参数(默认值=30)允许您使用更好的圆近似值,以内存和计算时间为代价(不知道这对您是否重要):
library(sf)
pt.df <- data.frame(pt = 1, x = 20, y = 20)
pt.sf <- st_as_sf(pt.df, coords = c("x", "y"))
get_area <- function(nq) {
circle1 <- st_buffer(pt.sf, dist = sqrt(100 / pi), nQuadSegs=nq)
st_area(circle1)
}
sapply(c(30,100,300,1000), get_area)
## [1] 99.95431 99.99589 99.99954 99.99996
如果你真的想要 恰好 100 的区域,那么你的问题(和@AdamTrevisan 的回答)建议的 'fudge' 是可行的方法(随着增加一百万的段数仍然只能让您到达 99.99999999997200461621 的区域)。要真正聪明,您可以使用area of an inscribed polygon的公式得出校正因子...
我想创建一个面积为 100 的圆作为 sf 对象。我以为 st_buffer() 会做,但面积是 略小于 100.
pt.df <- data.frame(pt = 1, x = 20, y = 20)
pt.sf <- st_as_sf(pt.df, coords = c("x", "y"))
circle1 <- st_buffer(pt.sf, dist = sqrt(100 / pi))
st_area(circle1) # 99.95431 on my PC
我可以用软糖因子乘以半径,我得到了我想要的。
fudge <- sqrt( 100 / st_area(circle1) )
circle2 <- st_buffer(pt.sf, dist = fudge * sqrt(100 / pi))
st_area(circle2) # 100
但使用软糖因素似乎很愚蠢。
有没有办法在 sf 包内创建一个已知区域的圆而不用 st_buffer 中的软糖因素 ?
用圆周率的截断形式计算半径是一个浮点精度问题。这将导致一个圆圈略小于您想要的输出。可以看到pi只能存储到机器精度:
.Machine$double.eps
# 2.22044604925031e-16
pi
# 3.14159265358979
如果你想修正它,你可以在你想要的区域使用线性修正。请注意,这仍然是一个近似值,但它应该让您更接近您想要的结果。
radius <- function(area){
A <- area + (area * 0.000457099999999997)
return(sqrt(A / pi))
}
system.file("shape/nc.shp", package="sf") %>%
st_read() %>%
st_centroid() %>%
st_transform(st_crs(5070)) %>%
st_buffer(radius(100)) %>%
st_area()
主要问题是 st_buffer
在内部使用多边形,而不是圆形。增加 nQuadSegs
参数(默认值=30)允许您使用更好的圆近似值,以内存和计算时间为代价(不知道这对您是否重要):
library(sf)
pt.df <- data.frame(pt = 1, x = 20, y = 20)
pt.sf <- st_as_sf(pt.df, coords = c("x", "y"))
get_area <- function(nq) {
circle1 <- st_buffer(pt.sf, dist = sqrt(100 / pi), nQuadSegs=nq)
st_area(circle1)
}
sapply(c(30,100,300,1000), get_area)
## [1] 99.95431 99.99589 99.99954 99.99996
如果你真的想要 恰好 100 的区域,那么你的问题(和@AdamTrevisan 的回答)建议的 'fudge' 是可行的方法(随着增加一百万的段数仍然只能让您到达 99.99999999997200461621 的区域)。要真正聪明,您可以使用area of an inscribed polygon的公式得出校正因子...