Search code examples
rgeometrybufferr-sf

Creating a Circle of Known Area


I want to create a circle of area 100 as an sf object. I thought st_buffer() would do it, but the area is slightly less than 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

I can use a fudge factor to multiply the radius and I get what I want.

fudge    <- sqrt( 100 / st_area(circle1) )
circle2  <- st_buffer(pt.sf, dist = fudge * sqrt(100 / pi))
st_area(circle2)  # 100

But it seems silly to use a fudge factor.

Is there a way to create a circle of known area within the sf package without a fudge factor in st_buffer ?


Solution

  • The main issue is that st_buffer works internally with polygons, not with circles. Increasing the nQuadSegs argument (default=30) allows you to use a better approximation to a circle, at the cost of memory and computation time (don't know if this is important to you):

    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
    

    If you really want an area of exactly 100, then the 'fudge' that your question (and @AdamTrevisan's answer) suggest is the way to go (as increasing the number of segments to a million still only gets you to an area of 99.99999999997200461621). To be really clever, you might be able to use the formula for the area of an inscribed polygon to come up with a correction factor ...