Source: https://gometry.monicz.dev/api/s2/

# S2

S2 spherical cells, fixed-level and adaptive covers, and hierarchical cell sets.

**Related:** [Grid-generic API](https://gometry.monicz.dev/api/grids/)

## Types

## S2Cell

Accepted call forms

```python
S2Cell(value: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str, /) -> S2Cell
```

```python
S2Cell(value: Point, /, *, level: int | integer[Any]) -> S2Cell
```

```python
S2Cell(value: FloatScalar | ScalarFloatArray, /, lat: FloatScalar | ScalarFloatArray, *, level: int | integer[Any]) -> S2Cell
```

One S2 cell: a level-addressed quadrilateral tile on the sphere.

Wraps the 64-bit cell id with typed accessors (`cell.level`,
`cell.token`, `cell.polygon`, `cell.center`) and hierarchy
moves (`parent`/`children`/`neighbors`). Convert via
`S2Cell(...)`, and back with `int(cell)`.

One S2 cell from an id, token, lon/lat pair, or point geometry.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `value` | `S2Cell \| (int \| integer[Any] \| ndarray[tuple[()], dtype[signedinteger[Any] \| unsignedinteger[Any]]]) \| str \| Point \| (FloatScalar \| ScalarFloatArray)` | An existing cell, id, token, longitude of a `lon, lat` pair, or a point geometry. | *required* |
| `level` | `int \| integer[Any]` | S2 level (`0`-`30`); required for coordinate construction. | `varies by call form` |
| `lat` | `FloatScalar \| ScalarFloatArray` | Latitude when `value` is a scalar longitude. | `varies by call form` |

Returns:

| Type | Description |
| --- | --- |
| `S2Cell` |  |

Raises:

| Type | Description |
| --- | --- |
| `ParseError` | If `value` is not a valid S2 cell id or token. |
| `GeometryError` | If `level` is out of range. |
| `InvalidGeometryError` | If a scalar coordinate is non-finite or out of range. |

### level `property`

```python
level: int
```

S2 level of this cell (`0`-`30`).

Returns:

| Type | Description |
| --- | --- |
| `int` |  |

### children

```python
children(level: int | None = None) -> CellArray[S2Cell]
```

Child cells at a finer level.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `level` | `int` | Target level; must not be coarser than this cell's. Defaults to one finer than this cell's level; a maximum-depth cell has no children and yields an empty CellArray. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `CellArray of S2Cell` | The descendant cells in the grid's public identity order. |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If the target depth is invalid for this cell. |

Examples:

```pycon
>>> import gometry as gm
>>> cell = gm.s2_cover(gm.Point(-122.4194, 37.7749, crs=4326), level=12)[0]
>>> assert cell is not None
>>> len(cell.children(13))
4
```

### children\_count

```python
children_count(level: int | None = None) -> int
```

Number of descendant cells at a finer level, counted closed-form without materializing them.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `level` | `int` | Target level; must not be coarser than this cell's. Defaults to one finer than this cell's level; a maximum-depth cell has no children and returns `0`. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `int` | The exact descendant count. |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If the target depth is coarser than this cell's, or invalid. |

Examples:

```pycon
>>> import gometry as gm
>>> cell = gm.s2_cover(gm.Point(-122.4194, 37.7749, crs=4326), level=12)[0]
>>> assert cell is not None
>>> cell.children_count(13)
4
```

### parent

```python
parent(level: int | None = None) -> S2Cell
```

Parent cell at a coarser level.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `level` | `int` | Target level; must not be finer than this cell's. Defaults to one coarser than this cell's level. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `S2Cell` | The ancestor cell. |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If the target depth is invalid for this cell. |

Examples:

```pycon
>>> import gometry as gm
>>> cell = gm.s2_cover(gm.Point(-122.4194, 37.7749, crs=4326), level=12)[0]
>>> assert cell is not None
>>> cell.parent(10).token
'808581'
```

## Functions

## s2\_bounding\_cell

```python
s2_bounding_cell(value: Geometry | GeometryArray[Geometry | None] | Iterable[float]) -> S2Cell
```

Return the deepest S2 cell that **provably** contains a geometry's lon/lat bounding box.

Sibling-consistent with `geohash_bounding_cell` / `tile_bounding_cell` /
`h3_bounding_cell`: non-point inputs collapse to their lon/lat envelope,
then the deepest cell that can be proven to cover that rectangle is returned.
Near cell boundaries the result may be one level coarser than the theoretical
deepest (always containing). A single point yields its exact level-30 leaf;
a multipoint uses its bounding box (same path as any multi-vertex region —
not a leaf-LCA of the vertices alone). Regions that span multiple cube faces
have no single containing cell and raise.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `value` | `Geometry, GeometryArray, or sequence of float` | A geometry/array (non-WGS84 frames reproject), or a bare lon/lat `(minx, miny, maxx, maxy)` bounds. | *required* |

Returns:

| Type | Description |
| --- | --- |
| `S2Cell` | The deepest cell that provably contains the geometry's bounding box; near boundaries may be coarser than theoretical deepest (always containing). |

Raises:

| Type | Description |
| --- | --- |
| `InvalidGeometryError` | If the geometry is empty or coordinates leave the lon/lat domain. |
| `GeometryError` | If no single S2 cell contains the bounds, or bare bounds are not ordered min <= max. |

Examples:

```pycon
>>> import gometry as gm
>>> berlin = gm.box(13.3, 52.4, 13.5, 52.6, crs=4326)
>>> gm.s2_bounding_cell(berlin).level
8
```

## s2\_cells

Accepted call forms

```python
s2_cells(values: GeometryArray[Point] | Iterable[Point] | (Sequence[Sequence[float]] | ndarray[tuple[int, int], dtype[floating[Any] | signedinteger[Any] | unsignedinteger[Any] | bool_]]) | FloatInput, lat: FloatInput | None = None, *, level: int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Iterable[int] | Buffer) -> CellArray[S2Cell]
```

```python
s2_cells(values: GeometryArray[Point | None] | Iterable[Point | None], lat: None = None, *, level: int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Iterable[int] | Buffer) -> CellArray[S2Cell | None]
```

Build S2 cells from points, coordinate rows, or parallel lon/lat columns.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `values` | `GeometryArray of Point, iterable of Point or None, coordinate matrix, or float` | Point geometries (`None` makes a missing cell), a coordinate matrix with at least two columns, or WGS84 longitudes. Iterable points must share one CRS and epoch; projected point inputs are reprojected to WGS84. Coordinate matrices use only X and Y, ignoring extra ordinates. | *required* |
| `lat` | `float or sequence of float` | WGS84 latitude per row when `values` supplies longitudes. Coordinate columns broadcast numpy-style; at least one must be a sequence. | `None` |
| `level` | `int or sequence of int` | S2 cell level (0-30; finer at higher values). A scalar broadcasts to every row; an array supplies one level per row. | *required* |

Returns:

| Type | Description |
| --- | --- |
| `CellArray of S2Cell` | One cell per input coordinate. |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If `level` is out of range or every argument is scalar. |
| `InvalidGeometryError` | If a coordinate is non-finite or columns differ in length. |

See Also

[S2Cell](https://gometry.monicz.dev/api/s2/#gometry.S2Cell "S2Cell") : Build a single cell.
[s2\_cover](https://gometry.monicz.dev/api/s2/#gometry.s2_cover "s2_cover") : Cover a geometry with S2 cells.

Examples:

```pycon
>>> import gometry as gm
>>> len(gm.s2_cells([21.0, 22.0], [52.0, 53.0], level=10))
2
```

## s2\_cover

Accepted call forms

```python
s2_cover(geom: GeometryArray[Geometry] | Iterable[Geometry], level: int | None = None, *, target_cells: int = 8, min_level: int | None = None, max_level: int | None = None, level_mod: Literal[1, 2, 3] = 1, cell_rule: Literal['center', 'within', 'overlap', 'bbox'] = 'overlap', max_cells: int | None = 1000000) -> Groups[CellArray[S2Cell]]
```

```python
s2_cover(geom: GeometryArray[Geometry | None] | Iterable[Geometry | None], level: int | None = None, *, target_cells: int = 8, min_level: int | None = None, max_level: int | None = None, level_mod: Literal[1, 2, 3] = 1, cell_rule: Literal['center', 'within', 'overlap', 'bbox'] = 'overlap', max_cells: int | None = 1000000) -> Groups[CellArray[S2Cell] | None]
```

```python
s2_cover(geom: Geometry, level: int | None = None, *, target_cells: int = 8, min_level: int | None = None, max_level: int | None = None, level_mod: Literal[1, 2, 3] = 1, cell_rule: Literal['center', 'within', 'overlap', 'bbox'] = 'overlap', max_cells: int | None = 1000000) -> CellArray[S2Cell]
```

Cover a geometry with fixed-level or adaptively refined S2 cells.

Omit `level` to adapt refinement to `target_cells` and the optional
level range. Supplying `level` fixes the output depth; non-default
adaptive controls cannot be combined with it. Keep the source geometry
separately and use the free `gm.*` predicates for exact geometry
questions.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `geom` | `Geometry, GeometryArray, or iterable of Geometry or None` | Geometry to cover (WGS84 lon/lat or projected). An array or iterable returns one group per input row; `None` produces a missing group and a present empty geometry produces an empty group. | *required* |
| `level` | `int` | Fixed S2 cell level (`0`-`30`; finer at higher values). The level is never coarsened to fit the output cap. Omit it for adaptive refinement. | `None` |
| `target_cells` | `int` | Approximate adaptive refinement target; the minimum admissible cover can exceed this value. | `8` |
| `min_level` | `int or None` | Coarsest adaptive S2 level allowed; omission uses level `0`. | `None` |
| `max_level` | `int or None` | Finest adaptive S2 level allowed; omission uses level `30`. | `None` |
| `level_mod` | `(1, 2, 3)` | Restrict adaptive output levels to increments from `min_level`. | `1` |
| `cell_rule` | `('within', 'center', 'overlap', 'bbox')` | Which cells to materialize, strictest to loosest. `'within'`: only cells entirely inside — cells the area fully owns. `'center'`: cells whose center is inside — unique assignment, balanced point binning. `'overlap'`: every cell touching the geometry — a complete-coverage superset, the safe default for candidate keys. `'bbox'`: every cell meeting the geometry's envelope, one window per part and split at the antimeridian — loosest and fastest. The rule never affects the exact predicates. | `'within'` |
| `max_cells` | `int or None` | Hard cap on returned cells, applied cumulatively across array groups. In fixed-level mode an insufficient cap raises; adaptive mode may coarsen only within its requested level range. Pass `None` for an unlimited cover. | `1000000` |

Returns:

| Type | Description |
| --- | --- |
| `CellArray or Groups of CellArray` | A scalar returns its coverage; an array returns one cell group per row. |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If the geometry, level, or a coverage parameter is invalid, if fixed and non-default adaptive controls are combined, or if the cover exceeds `max_cells`. |
| `InvalidGeometryError` | If a scalar geometry is empty or coordinates leave the lon/lat domain. |

See Also

[s2\_cells](https://gometry.monicz.dev/api/s2/#gometry.s2_cells "s2_cells") : Build S2 cells from lon/lat columns.
[s2\_cover](https://gometry.monicz.dev/api/s2/#gometry.s2_cover "s2_cover") : Cover with fixed or adaptive levels.

Examples:

```pycon
>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cov = gm.s2_cover(p, level=12)
>>> cell = cov[0]
>>> assert cell is not None
>>> (len(cov), cell.token)
(1, '8085809')
```

## s2\_difference

```python
s2_difference(left: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], right: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None]) -> CellArray[S2Cell]
```

Hierarchy-aware difference of two S2 cell sets.

Cells of `left` partially covered by `right` split into children
until the remainder is exact; the result is normalized in the grid's
public identity order.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `left` | `S2Cell, int, str, or iterable of those with missing values skipped` | The two cell sets (any mix of cell objects, ids, or tokens). | *required* |
| `right` | `S2Cell, int, str, or iterable of those with missing values skipped` | The two cell sets (any mix of cell objects, ids, or tokens). | *required* |

Returns:

| Type | Description |
| --- | --- |
| `CellArray of S2Cell` |  |

Raises:

| Type | Description |
| --- | --- |
| `ParseError` | If an id or token is not a valid S2 cell. |

Examples:

```pycon
>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cells = [cell for cell in gm.s2_cover(p, level=12) if cell is not None]
>>> len(gm.s2_difference(cells, []))
1
```

## s2\_intersection

```python
s2_intersection(left: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], right: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None]) -> CellArray[S2Cell]
```

Hierarchy-aware intersection of two S2 cell sets.

A cell survives where the two sets overlap; ancestor/descendant overlap
keeps the finer cell.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `left` | `S2Cell, int, str, or iterable of those with missing values skipped` | The two cell sets (any mix of cell objects, ids, or tokens). | *required* |
| `right` | `S2Cell, int, str, or iterable of those with missing values skipped` | The two cell sets (any mix of cell objects, ids, or tokens). | *required* |

Returns:

| Type | Description |
| --- | --- |
| `CellArray of S2Cell` |  |

Raises:

| Type | Description |
| --- | --- |
| `ParseError` | If an id or token is not a valid S2 cell. |

Examples:

```pycon
>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cells = [cell for cell in gm.s2_cover(p, level=12) if cell is not None]
>>> len(gm.s2_intersection(cells, cells))
1
```

## s2\_union

```python
s2_union(left: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], right: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None]) -> CellArray[S2Cell]
```

Hierarchy-aware union of two S2 cell sets.

Returns the normalized cell union in the grid's public identity
order: contained cells are absorbed by their ancestors and complete
sibling groups are merged into parents.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `left` | `S2Cell, int, str, or iterable of those with missing values skipped` | A single cell identity or a collection on either side (any accepted mix of cell objects and identity values for this grid). | *required* |
| `right` | `S2Cell, int, str, or iterable of those with missing values skipped` | A single cell identity or a collection on either side (any accepted mix of cell objects and identity values for this grid). | *required* |

Returns:

| Type | Description |
| --- | --- |
| `CellArray of S2Cell` |  |

Raises:

| Type | Description |
| --- | --- |
| `ParseError` | If an id or token is not a valid S2 cell. |

Examples:

```pycon
>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cells = [cell for cell in gm.s2_cover(p, level=12) if cell is not None]
>>> len(gm.s2_union(cells, cells))
1
```
