Skip to contents

A polygon crossing the antimeridian is usually written wrapped: 179.9 then -179.9. Read literally — which is what both coverage twins now do — that edge runs 359.8 degrees the WRONG way round the world, and the polygon is its own complement. Measured on global05: 7,196 cells instead of 4.

Usage

unwrap_ring(ring)

unwrap_polygon(x)

Arguments

ring

a two-column matrix (or anything as.matrix() gives one from) with longitude in column 1

x

an sf, sfc or sfg POLYGON / MULTIPOLYGON

Value

the same matrix with column 1 unwrapped

for unwrap_polygon(), the same object with every ring unwrapped

Details

The fix is one explicit rule, applied at the INPUT boundary and nowhere else. Walking the ring, whenever consecutive vertices differ by more than 180 degrees of longitude, carry -/+360 onward (the shortest-path convention). The first vertex is never moved, so the ring keeps whichever frame it arrived in; only the edges are made short. The result may leave [-180, 180] — that is the point, and it is what the g1 codec stores and what cells_in_polygon_grid() expects.

unwrap_polygon() applies the rule to every ring of a POLYGON or MULTIPOLYGON, each ring independently: a hole is a closed ring of its own and carries its own crossings, and a multipolygon part on each side of the line must stay on its own side rather than being dragged across by its neighbour.

Where this runs

At every input boundary — an upload, a drawn place, place_encode(), the sf-facing wrappers such as cells_in_polygon() — and never inside coverage. cells_in_polygon_grid() reads coordinates literally and guesses nothing; that is the whole reason this is a separate, exported, separately-fixtured function with a TypeScript twin (unwrapRing()), rather than a heuristic buried in the geometry.

The limit

A ring that genuinely spans more than 180 degrees of longitude cannot be expressed wrapped: every one of its long edges is indistinguishable from a crossing, and the shortest-path convention will turn it inside out. Such a ring must arrive already unwrapped. This is a property of the wrapped representation, not of this implementation.

Examples

r <- cbind(c(179.9, -179.9, -179.9, 179.9, 179.9), c(50, 50, 50.05, 50.05, 50))
unwrap_ring(r)[, 1]   # 179.9 180.1 180.1 179.9 179.9
#> [1] 179.9 180.1 180.1 179.9 179.9