Search code examples
rspatialr-sf

How can I buffer a set of polygons with common edges only outwards (from the edges, not the inner "frontiers")


I am doing this with the map of the counties of the Republic of Ireland found here (direct download).

What I want to do is extend the coast of every county towards the sea for 1500 m, but without moving the borders between counties.

EDIT: I also need to extend the county boundaries of the coastal counties towards the sea, in order to meet the new, extended "coastline" (added as per mrhellmann question)

My code so far:

  1. Load the packages and data

    library(tidyverse)
    library(sf)
    library(ggplot2)
    
    ireland <- st_read("~Census2011_Admin_Counties_generalised20m.shp")
    st_crs(ireland) # units are m
    
  2. Buffer

    ireland_buf <- ireland %>% 
      st_buffer(1500)
    
  3. Check how borders have moved

    ggplot() + 
      geom_sf(data = ireland_buf, col = "red", fill = "lightgray") + 
      geom_sf(data = ireland, col = "black", fill = NA) + 
      theme_bw()
    

enter image description here

So, basically, the red line corresponds to the buffered map and the black one to the original map. I've obtained the desired effect in the coastline, but then county borders are moved inwards or outwards depending on the county


Solution

  • Using the base shapefile you can create a few other objects along the way by combining, buffering, spatial sampling, and creating voronoi polygons. By joining and intersecting these you can get most of the way there.

    Problems with the below example are that it buffers into the adjacent land at the northern border, and some of the islands that are close together may cause very small problems.

    library(sf)
    library(ggplot2)
    library(dplyr)
    library(tmap) # for "MAP_COLORS"
    
    
    path <- 'Census2011_Admin_Counties_generalised20m.shp'
    ireland <- read_sf(path)
    
    base_crs <- st_crs(ireland)
    
    #remove county lines, keeps only an outline of the area
    ireland_combined <- st_combine(ireland)  
    # add buffer, fortunately the base CRS is in meters
    ireland_buffered <- st_buffer(ireland_combined, 1500)   
    # polygon(s) of only the buffer
    buffer_only <- st_difference(ireland_buffered, ireland_combined) 
    
    # sample points inside the buffer for voronoi polygons
    # type can be random, hexagonal, or regular.
    # increase size for accuracy, decrease for speed.
    sampled_points <- st_sample(buffer_only, size = 5000, type = 'hexagonal')
    
    #Create voronoi polygons from sampled points
    voronoi <- sampled_points %>%
      st_union() %>%
      st_voronoi() %>%
      st_cast()
    
    # Crop voronoi polygons to buffer area only
    # plot 2 below shows voronoi polygons extend much too far for this case
    voronoi_buffer <- 
      st_intersection(
        st_make_valid(voronoi),
        buffer_only
      ) %>%
      st_as_sf()
    
    # Join polygons by proximity to counties in base shapefile
    voronoi_joined <- st_join(
      voronoi_buffer,
      ireland,
      join = st_nearest_feature
    )
    
    # join voronoi polygons by countyname
    vj_summarized <- voronoi_joined %>% 
      group_by(COUNTYNAME) %>%
      summarize()
    
    #mapview was having trouble with this county name
    vj_summarized$COUNTYNAME[6] <- 'Dun Laoghaire-Rathdown'
    ireland$COUNTYNAME[20] <- 'Dun Laoghaire-Rathdown'
    
    #rbind sea buffer & land, then join with group_by & summarize
    buffer_and_land <- rbind(ireland %>% select(COUNTYNAME), vj_summarized) %>%
      group_by(COUNTYNAME) %>%
      summarise()
    
    
    #plots
    p1 <- ggplot() + 
      geom_sf(data = ireland, fill = NA) + 
      geom_sf(data = ireland_buffered, fill = NA, alpha = .4, color = 'green') +
      ggtitle(label = "Ireland counties & 1500m buffer")
    
    p2 <- ggplot() + 
      geom_sf(data = voronoi, alpha = .2) +
      geom_sf(data = voronoi_buffer, alpha = .2, fill = NA, color = 'turquoise') +
      ggtitle(label = 'Voronoi & buffer')
    
    # Counties & buffers joined & colored
    p4 <- tm_shape(st_geometry(buffer_and_land)) + 
      tm_polygons(col = "MAP_COLORS") + 
      tm_shape(st_geometry(ireland)) + 
      tm_polygons(border.col = 'black', col = NA, alpha = 0)
    
    p1
    

    Green area shows buffer & problem with buffering into bordering land. You can avoid this by using a shapefile of the whole island.

    p2
    

    Full voronoi shown. Area of interest in turquoise.

    p4
    

    Buffer joined with counties, and filled by countyname. County borders are overlaid in black.

    A close-up of one of the messier areas, increasing the number of sample points may help. enter image description here Created on 2021-03-22 by the reprex package (v1.0.0)