How to Simplify Polygons of SF Object in R

rsfsimplify

How do I simplify an sf polygon without introducing gaps and slivers?

With a shapefile, for example, I would use rmapshaper::ms_simplify():

library("pryr")
library("rgdal")
library("rmapshaper")

download.file("https://borders.ukdataservice.ac.uk/ukborders/easy_download/prebuilt/shape/England_gor_2011.zip",
              destfile = "regions.zip")
unzip("regions.zip")
regions <- readOGR(".", "england_gor_2011")
object_size(regions)
# ~13MB

regions <- ms_simplify(regions)
object_size(regions)
# < 1MB

I've tried sf::st_cast() which, from the man pages, states:

Cast geometry to another type: either simplify, or cast explicitly

and:

to argument: character; target type, if missing, simplification is tried; when x is of type sfg (i.e., a single geometry) then to needs to be specified.

When I've left to as missing this hasn't worked as expected though (I knew it was too good to be true!):

library("sf")
regions <- sf::read_sf("england_gor_2011.shp")
object_size(regions)
# ~13MB

regions <- sf::st_cast(regions)
object_size(regions)
# Still 13MB

Currently I'm opening the file with rgdal::readOGR(), simplifying it, saving this, then loading this again with sf.

Is there a better way?


rgeos::gSimplify()

@s.k 's suggestion of rgeos::gSimplify() can do topologically-aware simplifications (i.e. simplifies without creating slivers) when specified with the following arguments:

library("rgeos")
regions_gSimplify <- gSimplify(regions, tol = 0.05, topologyPreserve = TRUE)

gSimplify doesn't preserve the @data frame, though, so we should re-create it:

regions_df <- regions@data
regions_gSimplify <- sp::SpatialPolygonsDataFrame(regions_gSimplify, regions_df)

And this does indeed result in a smaller file size (can tweak tol argument to get it smaller) and I confirmed this hadn't created any slivers by examining it in QGIS.

object_size(regions_gSimplify)
# ~8MB

So although this is a valid alternative to rmapshaper::ms_simplify() I still have the same problem, namely that it doesn't work with sf:

regions_sf <- sf::read_sf("england_gor_2011.shp")
object_size(regions_sf)

regions_gSimplify <- gSimplify(regions_sf, topologyPreserve = TRUE, tol = 0.05)
# Error in gSimplify(regions_sf, topologyPreserve = TRUE, tol = 0.05) : 
# no slot of name "proj4string" for this object of class "sf"

@obrl_soil 's answer can also be applied to gSimplify(), just use it in place of ms_simplify().

Best Answer

You can cast an sf object to sp, for packages that don't yet support sf - I do this a fair bit for raster/polygon interactions. So you could do:

simplepolys <- rmapshaper::ms_simplify(input = as(sfobj, 'Spatial')) %>%
  st_as_sf()
Related Question