Skip to contents

Returns, for each polygon feature, an a5_covering of the A5 cells at resolution that belong to it. By default a cell belongs when its centre lies inside the polygon; set containment = "overlapping" to also include every cell that touches the polygon boundary, giving gap-free coverage.

Usage

a5_polygon_to_cells(x, resolution, containment = c("centre", "overlapping"))

Arguments

x

A polygon-like geometry. One of:

  • Any geometry handleable by wk::wk_handle() (e.g. wk::wkt(), wk::wkb(), wk::rct(), sf, sfc) containing one or more POLYGON / MULTIPOLYGON features.

  • A SpatVector of polygons (requires the terra package).

  • A two-column numeric matrix (cbind(lon, lat)) of vertices, interpreted as a single outer ring.

  • A data.frame with columns lon and lat, interpreted as a single outer ring.

resolution

Integer scalar target resolution (0-30).

containment

Character scalar selecting which cells to include. "centre" (the default; "center" also works) includes a cell when its centre lies inside the polygon. "overlapping" additionally includes every cell that overlaps the polygon boundary, so the result fully covers the polygon. The overlapping set is a superset of the centre set.

Value

An a5_covering vector with one element per feature (one for matrix and data.frame input). NA for a missing geometry; an empty covering for an empty geometry or one too small to hold a cell centre.

Details

The result has one covering per feature, so it can be added as a column: sf_data$cells <- a5_polygon_to_cells(sf_data, 10). A feature's parts (a MULTIPOLYGON) and holes are combined into its covering, with hole interiors excluded. To combine features into one covering, use a5_union(). Coverings are compacted; use a5_uncompact() for the cells at resolution.

With containment = "centre", membership is determined by centre-point containment: a cell is included if its centroid lies inside the polygon, with hole interiors excluded. Cells straddling the boundary whose centre falls outside are dropped, so the union of the cells does not fully cover the polygon.

With containment = "overlapping", every cell that contains any point of the polygon boundary is kept as well, so the returned cells cover the polygon without gaps. Hole boundaries count as boundary, so cells straddling a hole edge are included while the hole interior is still excluded.

Coordinates must be WGS 84 longitude/latitude in degrees. Rings are closed automatically; a trailing duplicate vertex is dropped if present.

Polygon edges are great-circle arcs, the shortest path on the sphere between consecutive vertices, as in the other A5 libraries and in s2. An edge between two points on the same parallel therefore bows toward the pole: a 20 degree edge along 10 degrees north peaks at 10.15 degrees north, and a 1 degree edge at that latitude bows by about 40 m. An edge whose ends are antipodal has no single great circle and gives an undefined result. Where edges should follow parallels, as in GeoJSON (RFC 7946 defines its edges as straight in longitude and latitude), add vertices along them first.

A wk::rct() box is a polygon with great-circle edges like any other, with two exceptions: a box covering the whole globe (wk::rct(-180, -90, 180, 90)) gives every cell, and a box reaching from pole to pole keeps its west and east edges on their meridians. Any other box spanning 180 degrees of longitude or more is an error, because its top and bottom edges would run the short way round the globe.

Rings must not cross themselves. A self-intersecting ring, such as a bow tie, gives an undefined result that can include cells thousands of kilometres outside the polygon. Because edges are great circles, a ring that is valid in longitude and latitude can still cross itself on the sphere; s2::s2_is_valid() checks rings with the same edge model.

Where no A5 cell centroids at the specified resolution fall within the geometry, its covering is empty.

Matrix and data.frame inputs are treated as a single ring; for multi-feature data or polygons with holes, pass an sf, sfc, wk, or SpatVector geometry instead.

Examples

poly <- wk::wkt(
  "POLYGON ((-3.3 55.9, -3.1 55.9, -3.1 56, -3.3 56, -3.3 55.9))"
)
covering <- a5_polygon_to_cells(poly, resolution = 8)
covering
#> <a5_covering[1]>
#> [1] res 8: empty
a5_uncompact(covering)
#> <a5_cell[0]>

# Gap-free coverage: every cell touching the polygon
a5_polygon_to_cells(poly, resolution = 8, containment = "overlapping")
#> <a5_covering[1]>
#> [1] res 8: 2 cells (2 stored)

# One covering per feature
polys <- wk::wkt(c(
  "POLYGON ((-3.3 55.9, -3.1 55.9, -3.1 56, -3.3 56, -3.3 55.9))",
  NA,
  "POLYGON ((-4.3 55.8, -4.2 55.8, -4.2 55.9, -4.3 55.9, -4.3 55.8))"
))
a5_polygon_to_cells(polys, resolution = 8)
#> <a5_covering[3]>
#> [1] res 8: empty <NA>         res 8: empty