rr-sfterra

Why does terra in R find the buffer of a polygon to not fully contain the buffer of the centroid?


I am using the terra package in R to get a buffer around polygons. However, I have noticed that terra::buffer() is not behaving as expected. In particular, for some polygons and buffer widths the buffered polygon does not fully contain a buffer with the same width of its centroid. Is there a bug or am I doing something wrong? I provide reproducible code below and a picture of the nonintuitive area difference. Thanks for your help!

Buffer Differences

library(terra)

# Define the vertices of the triangle (right-angled at point (0, 0))
vertices <- rbind(
  c(-93.746, 46.99),     # Vertex 1 (right angle)
  c(-93.736, 46.99),    # Vertex 2 (height of 10 units)
  c(-93.746, 46.9886),     # Vertex 3 (base of 5 units)
  c(-93.746, 46.99)      # Close the polygon by returning to Vertex 1
)

# Create the triangle polygon - EPSG:4269 is lat/lon
triangle_polygon <- vect(vertices, type = "polygons", crs = "EPSG:4269")

# Plot the rectangular triangle
plot(triangle_polygon)

# Get centroids
centroid <- terra::centroids(triangle_polygon)

# Buffer centroid (5000m)
centroid_buffer <- terra::buffer(centroid, 5000)

# Buffer polygon (5000m)
buffer <- terra::buffer(triangle_polygon, 5000)

# Do buffered centroid minus buffered polygon
diff <- terra::erase(centroid_buffer, buffer)

# The picture shows area contained in the buffered centroid, but not in the buffered polygon
plot(diff)

Posted as an issue in the terra github: Here.


Solution

  • **Updated answer as issue no longer occurs using terra_1.8-15

    The issue seems to relate to a previous version, and now your code works with terra_1.8-15. However, you will need to switch the order that you call your buffers in terra::buffer().

    library(terra)
    library(ggplot2)
    library(tidyterra)
    
    # Define the vertices of the triangle (right-angled at point (0, 0))
    vertices <- rbind(
      c(-93.746, 46.99),
      c(-93.736, 46.99),
      c(-93.746, 46.9886),
      c(-93.746, 46.99)
    )
    
    # Create the triangle polygon - EPSG:4269 is lat/lon
    triangle_polygon <- vect(vertices, type = "polygons", crs = "EPSG:4269")
    
    # Get centroids
    centroid <- centroids(triangle_polygon)
    
    # Buffer centroid (5000m)
    centroid_buffer <- buffer(centroid, 5000)
    
    # Buffer polygon (5000m)
    buffer <- buffer(triangle_polygon, 5000)
    
    # Do buffered centroid minus buffered polygon
    diff <- erase(buffer, centroid_buffer)
    
    ggplot() +
      geom_spatvector(data = diff) +
      scale_x_continuous(
        breaks = round(seq(ext(diff)[1], ext(diff)[2], length.out = 5), 2)
        ) + 
      theme(panel.background = element_blank())
    

    1

    If you need a smoother edge to your buffers, use the quadsegs parameter, which controls the number of segments per quarter circle the default is 10):

    centroid_buffer <- buffer(centroid, 5000, quadsegs = 1000)
    buffer <- buffer(triangle_polygon, 5000, quadsegs = 1000)
    diff <- erase(buffer, centroid_buffer)
    
    ggplot() +
      geom_spatvector(data = diff) +
      scale_x_continuous(
        breaks = round(seq(ext(diff)[1], ext(diff)[2], length.out = 5), 2)
      ) + 
      theme(panel.background = element_blank())
    

    2