An overlay can leave a line or a point behind: inside a GEOMETRYCOLLECTION, or – where everything left of a polygon is a shared edge – as a bare LINESTRING sitting directly in the geometry column. Only the polygons carry area, and the residue causes trouble downstream out of all proportion to the area it represents, which is none.

keep_polygons(x)

Arguments

x

sf object

Value

sf object holding only non-empty POLYGON / MULTIPOLYGON features. May have more rows than x, when a collection held several polygons, and fewer when a feature had no polygonal part at all.

Why testing for GEOMETRYCOLLECTION is not enough

Two polygons that share only an edge intersect in a bare LINESTRING, with no collection anywhere in the result, so a guard written as if (any(st_geometry_type(x) == "GEOMETRYCOLLECTION")) never fires.

The way that fails is worth spelling out. terra::vect() drops a non-polygon geometry while keeping its attribute row, so the SpatVector comes back carrying more attribute rows than it has geometries. Nothing complains at the time; the next thing to touch the attributes fails instead, a long way from the cause. Measured on one tile of a real study area, exactly one of 6,871 differences was such a line, and it surfaced two function calls later as [as,sf] coercion failed.

Filtering in sf is what makes this safe: attributes are columns of the same data frame as the geometry, so they cannot desynchronise.