Search code examples
rspatial

spatial operations: how to buffer a polygon by area, not distance (from edge)


I want to buffer a spatial polygon P of area A so that the buffered feature P_buffered attains a defined area A_buffered. Function sf::st_buffer grows a feature by distance d (from edge) but not by area.

So far, I tried:

  1. approximating d from some radial measure of P (radius of enclosing circle, diagonal of bbox), assuming that a radial increase by factor x will inflate the area by x² (roughly, depending on P's rotational symmetry).
  2. approximating d iteratively by interval halving

The accuracy of (1) varies with the feature's shape while (2) is too slow (at least my implementation of it)

I'd be very grateful for hints towards some package with a corresponding function (pseudo::buffer_area()) or codewise solution I seem to be overlooking.

Re. accessing other GIS executables from R, please note that the code has to run on a machine where only the availability of R packages can be taken as granted.

example data: dput dump of an example polygon class sfcfor use with {sf}: https://gist.github.com/1O/bc3798468b48f19ab2533f16c99c2268


Solution

  • I still suggest a while loop (your second approach)... perhals someone can tweak the growth/decline aloritm...

    library(sf)
    
    points = matrix(c(0,0,20,0,10,10,0,10,0,0),ncol=2, byrow=TRUE)
    pts = list(points)
    pl1 = st_polygon(pts)
    
    # x = polygon
    # y = desired buffer area
    # z = allowed delte from area 
    # example y = 100, z = 0.01, the bufferarea aloowed is bewteen 99 and 101
    buffer_distance <- function(x, y, z) {
      buff_dist <- 1
      buffer_area <- st_area(st_buffer(x, buff_dist)) - st_area(x)
      while (!data.table::between(buffer_area, 
                                  y - z * y,
                                  y + z * y)) {
        if (buffer_area > y) {
          buff_dist <- buff_dist * (1 - y / st_area(st_buffer(x, buff_dist) ))
        } else {
          buff_dist <- buff_dist * (1 + y / st_area(st_buffer(x, buff_dist) ))
        }
        buffer_area <- st_area(st_buffer(x, buff_dist)) - st_area(x)
      }
      return(buff_dist)
    }
    
    buffer_distance(pl1, 100, 0.01)
    #[1] 1.688372
    
    st_area(st_buffer(pl1, buffer_distance(pl1, 100, 0.01))) - st_area(pl1)
    # [1] 100.3634
    
    plot(st_buffer(pl1, buffer_distance(pl1, 100, 0.01)))
    plot(pl1, add = TRUE)   
    

    enter image description here