Skip to content

Grids & cells

Grid-generic cells and the shared columnar CellArray container.

Related: Grids & geocodes

Cell

One cell of a discrete global grid — the uniform surface every cell class (H3Cell, S2Cell, ...) satisfies structurally, without inheriting it. Annotate grid-system-agnostic code with this protocol::

import gometry as gm

def ring_area(cell: gm.Cell) -> float:
    return sum(neighbor.area for neighbor in cell.neighbors)

Each system keeps its own named depth property (resolution, level) next to this shared surface; parent/children accept the depth positionally, defaulting to one step coarser/finer.

contains/intersects are typed to accept a cell or its token. Numeric-id systems also accept int ids on the concrete classes; the protocol leaves that out so token-based systems satisfy it too.

area property

area: float

Area of the cell in square meters, using its grid's area model.

H3 and S2 use an authalic sphere; geohash and tile use the WGS 84 ellipsoidal area of their lon/lat rectangle.

Returns:

Type Description
float

Area in square meters.

center property

center: Point

Cell center as a WGS 84 Point (lon/lat).

Returns:

Type Description
Point

The center in longitude/latitude coordinates.

neighbors property

neighbors: CellArray[Self]

The edge-adjacent cells at this cell's depth.

How many there are is system-specific, and can vary with position: six around an H3 hexagon but five around a pentagon, four for S2, eight for geohash away from the poles.

Returns:

Type Description
CellArray[Self]

The edge-adjacent cells at the same depth.

polygon property

polygon: Polygon

The cell as a filled WGS 84 Polygon (lon/lat).

H3 and S2 emit planar chord proxies for their true spherical edges.

Returns:

Type Description
Polygon

The filled cell boundary in longitude/latitude coordinates.

token property

token: str

The cell id in its system's canonical lowercase text form.

Returns:

Type Description
str

Canonical text for this cell.

children

children(depth: int | None = None, /) -> CellArray[Self]

Child cells at a finer depth, one step finer by default.

Parameters:

Name Type Description Default
depth int or None

Requested child depth; None selects one step finer.

None

Returns:

Type Description
CellArray[Self]

The cells at the requested depth.

children_count

children_count(depth: int | None = None, /) -> int

Count descendants at a finer depth without materializing them.

Parameters:

Name Type Description Default
depth int or None

Requested child depth; None selects one step finer.

None

Returns:

Type Description
int

Number of cells at the requested depth.

contains

contains(other: Self | str) -> bool

Whether this cell is, or is an ancestor of, other.

Parameters:

Name Type Description Default
other Self or str

A cell or its canonical token.

required

Returns:

Type Description
bool

Whether other is this cell or one of its descendants.

intersects

intersects(other: Self | str) -> bool

Whether this cell and other share any area.

Hierarchy cells cannot partially overlap, so this holds exactly when one of the two contains the other.

Parameters:

Name Type Description Default
other Self or str

A cell or its canonical token.

required

Returns:

Type Description
bool

Whether the two cells overlap hierarchically.

parent

parent(depth: int | None = None, /) -> Self

Parent cell at a coarser depth, one step coarser by default.

Parameters:

Name Type Description Default
depth int or None

Requested parent depth; None selects one step coarser.

None

Returns:

Type Description
Self

The ancestor at the requested depth.

CellArray

Accepted call forms
CellArray(values: Iterable[H3Cell], *, type: None = None) -> CellArray[H3Cell]
CellArray(values: Iterable[S2Cell], *, type: None = None) -> CellArray[S2Cell]
CellArray(values: Iterable[GeohashCell], *, type: None = None) -> CellArray[GeohashCell]
CellArray(values: Iterable[Tile], *, type: None = None) -> CellArray[Tile]
CellArray(values: Iterable[H3Cell | None], *, type: None = None) -> CellArray[H3Cell | None]
CellArray(values: Iterable[S2Cell | None], *, type: None = None) -> CellArray[S2Cell | None]
CellArray(values: Iterable[GeohashCell | None], *, type: None = None) -> CellArray[GeohashCell | None]
CellArray(values: Iterable[Tile | None], *, type: None = None) -> CellArray[Tile | None]
CellArray(values: Iterable[GeohashCell | str], *, type: type[GeohashCell]) -> CellArray[GeohashCell]
CellArray(values: Iterable[GeohashCell | str | None], *, type: type[GeohashCell]) -> CellArray[GeohashCell | None]
CellArray(values: ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str], *, type: type[H3Cell]) -> CellArray[H3Cell]
CellArray(values: Iterable[H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], *, type: type[H3Cell]) -> CellArray[H3Cell | None]
CellArray(values: ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str], *, type: type[S2Cell]) -> CellArray[S2Cell]
CellArray(values: Iterable[S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], *, type: type[S2Cell]) -> CellArray[S2Cell | None]
CellArray(values: ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str], *, type: type[Tile]) -> CellArray[Tile]
CellArray(values: Iterable[Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None], *, type: type[Tile]) -> CellArray[Tile | None]

An immutable array of one grid cell type backed by a shared uint64 id column.

Index with an integer for the typed cell object or None for a missing row; slice or mask for a new CellArray.

Build from an iterable containing typed cell objects and None; the grid is inferred from the first concrete cell. Raw ids for H3, S2, and Tile and tokens for any grid may accompany a typed cell of the same grid. Geohash cells use tokens, not integer ids. For raw-only values, numeric arrays or buffers, empty input, or all-missing input, pass type= explicitly; every id and token is validated.

Parameters:

Name Type Description Default
values Iterable[H3Cell] | Iterable[S2Cell] | Iterable[GeohashCell] | Iterable[Tile] | Iterable[H3Cell | None] | Iterable[S2Cell | None] | Iterable[GeohashCell | None] | Iterable[Tile | None] | Iterable[GeohashCell | str] | Iterable[GeohashCell | str | None] | (ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str]) | Iterable[H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None] | (ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[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] | (ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer | Iterable[Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str]) | Iterable[Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | None]

Typed cell objects and None; raw ids for H3/S2/Tile and tokens for any grid are accepted when a concrete cell supplies the family.

Typed cells and None; raw ids for H3/S2/Tile and string tokens are accepted when the grid can be inferred. Iterables are consumed in input order.

required
type None | type[GeohashCell] | type[H3Cell] | type[S2Cell] | type[Tile]

Native cell class. Inferred from a concrete typed cell when present.

One of the native H3Cell, S2Cell, Tile, or GeohashCell classes, required when the grid cannot be inferred.

varies by call form

Construct a cell array from typed cells, ids, or tokens.

The grid is inferred from a typed cell when type=None. H3, S2, and Tile ids and any grid's tokens may accompany a typed cell from the same grid; Geohash cells accept tokens, not integer ids. Supply type= for raw-only values, numeric arrays or buffers, empty input, or input containing only None. Wrap a single cell or supported id in an iterable; a bare token string is ambiguous, so wrap one token in a one-item iterable.

Returns:

Type Description
CellArray

An immutable array in input order; None entries remain missing rows.

Raises:

Type Description
TypeError

If the grid cannot be inferred, type is unsupported, or a value does not match the selected grid.

ParseError

If an id or token is invalid for the selected grid.

area property

area: NDArray[float64]

Cell areas in square meters, using each grid's area model.

H3 and S2 use an authalic-sphere model. Geohash and tile use the WGS 84 ellipsoidal area of their lon/lat rectangle.

Returns:

Type Description
ndarray

One float64 area (m²) per cell.

Examples:

>>> import gometry as gm
>>> arr = gm.CellArray([gm.Tile(x=0, y=0, zoom=0)])
>>> float(arr.area[0]) > 1e14
True

center property readonly

center: GeometryArray[Point | None]

Center points of every cell, as a packed WGS84 point array.

Computes each center from its cell id and allocates a packed point coordinate column.

Returns:

Type Description
GeometryArray

Dense cells yield one Point (lon/lat, OGC:CRS84) per row. Missing rows in a nullable CellArray remain None.

Examples:

>>> import gometry as gm
>>> arr = gm.CellArray([gm.Tile(x=0, y=0, zoom=1)])
>>> arr.center.to_wkt()[0]
'POINT (-90 42.5255643899033)'

grid property

grid: Literal['h3', 's2', 'tile', 'geohash']

Grid-system token for the stored cell type.

Returns:

Type Description
str

is_missing property

is_missing: NDArray[bool_]

Per-row missing mask as a read-only boolean NumPy array.

Bulk cell factories set a missing row when the corresponding geometry was missing; dense constructions return all-False.

Returns:

Type Description
numpy.ndarray of bool

nbytes property

nbytes: int

Logical id payload in bytes (len * 8).

Returns:

Type Description
int

neighbors property readonly

neighbors: Groups[CellArray[Cell] | None]

The edge-adjacent cells of every cell, as ragged rows.

Returns:

Type Description
Groups of CellArray

One row of neighbors per input cell, in input order. Neighbor counts vary, so the result is a Groups, not a rectangular CellArray. Missing input rows yield None groups.

polygon property readonly

polygon: GeometryArray[Polygon | None]

Filled WGS84 polygon of every cell, as a geometry array.

Returns:

Type Description
GeometryArray

Dense cells yield one polygon per row. Missing rows in a nullable CellArray remain None.

token property readonly

token: list[str] | list[str | None]

Canonical string token of every cell, in order.

For H3, S2, and tiles this is the text form of the numeric id exposed by to_numpy(). For Geohash it is the public string identity itself; Geohash to_numpy() instead returns typed GeohashCell objects.

Returns:

Type Description
list of str or None

One canonical token per logical row (H3 hex, S2 token, geohash, or tile quadkey); None marks a missing row.

Examples:

>>> import gometry as gm
>>> gm.CellArray([gm.GeohashCell('u33d')]).token
['u33d']

__bool__

__bool__() -> bool

False only when the array is empty.

Returns:

Type Description
bool

__contains__

__contains__(value: object, /) -> bool

Whether a cell id / cell object appears in the array.

Returns:

Type Description
bool

__getitem__

Accepted call forms
__getitem__(index: ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]], /) -> CellT_co
__getitem__(index: ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]], /) -> CellArray[CellT_co]
__getitem__(index: ndarray[tuple[int], dtype[bool_]], /) -> CellArray[CellT_co]
__getitem__(index: ndarray[tuple[Any, ...], dtype[signedinteger[Any] | unsignedinteger[Any]]], /) -> CellT_co | CellArray[CellT_co]
__getitem__(index: SupportsIndex, /) -> CellT_co
__getitem__(index: slice, /) -> CellArray[CellT_co]
__getitem__(index: Sequence[bool], /) -> CellArray[CellT_co]
__getitem__(index: Sequence[SupportsIndex] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer, /) -> CellArray[CellT_co]
__getitem__(index: SupportsIndex | slice | ndarray[tuple[int], dtype[bool_]] | Sequence[bool] | (Sequence[SupportsIndex] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | Buffer) | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[int], dtype[signedinteger[Any] | unsignedinteger[Any]]] | ndarray[tuple[Any, ...], dtype[signedinteger[Any] | unsignedinteger[Any]]], /) -> CellT_co | CellArray[CellT_co]

Select logical rows by integer, slice, or fancy index.

An int returns one cell object or None for a missing row. A slice or fancy index returns a CellArray of the same cell kind.

Returns:

Type Description
Cell, None, or CellArray

__iter__

__iter__() -> CellArrayIterator[CellT_co]

Iterate logical rows in row order, yielding cells or None.

Returns:

Type Description
iterator of Cell or None

__len__

__len__() -> int

Number of logical rows, including missing rows.

Returns:

Type Description
int

__reversed__

__reversed__() -> CellArrayIterator[CellT_co]

Iterate cells in reverse row order.

Returns:

Type Description
iterator of Cell

children

Accepted call forms
children(depth: int | None = None, /) -> Groups[CellArray[CellT]]
children(depth: int | None = None, /) -> Groups[CellArray[CellT] | None]

Return the child cells of every cell at a finer depth, as ragged rows.

Parameters:

Name Type Description Default
depth int

Target depth; must not be coarser than any input cell. Defaults to one finer than each cell's own depth.

None

Returns:

Type Description
Groups of CellArray

One row of children per input cell, in input order. Missing input rows yield None groups.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> children = gm.CellArray([cell]).children(8)[0]
>>> assert children is not None
>>> len(children)
7

children_count

children_count(depth: int | None = None, /) -> NDArray[float64]

Number of descendant cells each cell has at depth.

The columnar mirror of the scalar children_count — the count only, without materializing the children (which children does, as ragged rows). Present-row counts are integer-valued but are represented as float64; they are exact only when within float64's integer precision. Missing rows are represented by NaN to preserve row alignment.

Parameters:

Name Type Description Default
depth int

Target depth (resolution / level / precision / zoom). Omitted means one step finer than each cell.

None

Returns:

Type Description
ndarray

Read-only float64 numpy.ndarray of shape (n,); missing rows are NaN.

Raises:

Type Description
GeometryError

If depth is out of range for the grid.

See Also

children : The descendant cells themselves, as ragged rows.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> cells = gm.CellArray([cell, neighbor])
>>> cells.children_count(9).tolist()
[49.0, 49.0]

compact

compact(depth: int | None = None, /) -> CellArray[CellT]

Compact this cell set to the coarsest exact covering, returned in the grid's public identity order. Duplicates and descendants are absorbed; complete sibling groups merge recursively, stopping at depth. Missing rows are skipped; this is a set operation over present cells.

Parameters:

Name Type Description Default
depth int

Coarsest depth allowed.

None

Returns:

Type Description
CellArray

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> cells = gm.CellArray([cell, neighbor])
>>> len(cells.compact(5))
2

contains

Accepted call forms
contains(other: H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[H3Cell | None]) -> NDArray[bool_]
contains(other: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[S2Cell | None]) -> NDArray[bool_]
contains(other: GeohashCell | str | CellArray[GeohashCell | None]) -> NDArray[bool_]
contains(other: Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[Tile | None]) -> NDArray[bool_]

Test whether every row hierarchically contains the paired cell.

Parameters:

Name Type Description Default
other same-grid cell, integer id, string token, or CellArray

One identity broadcast to every row, or a same-length array.

required

Returns:

Type Description
ndarray

One read-only boolean per row.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> gm.CellArray([cell, neighbor]).contains(cell).tolist()
[True, False]

count

count(value: object) -> int

Number of elements with the same cell id.

Parameters:

Name Type Description Default
value cell object or int id

The element to count.

required

Returns:

Type Description
int

factorize

factorize() -> tuple[ndarray[tuple[int], dtype[int64]], CellArray[CellT]]

Factorize cells into dense integer codes and first-seen uniques (pandas factorize parity).

Returns:

Type Description
tuple

(codes, unique_cells) where codes is a read-only int64 ndarray and unique_cells is a CellArray.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> codes, unique = gm.CellArray([cell, neighbor]).factorize()
>>> codes.tolist()
[0, 1]

index

index(value: object, start: int = 0, stop: int | None = None) -> int

First index of an equal cell id in [start, stop).

Parameters:

Name Type Description Default
value cell object or int id

The element to locate.

required
start int

First position searched.

0
stop int

One past the last position searched (the array length when omitted).

None

Returns:

Type Description
int

The first matching position.

Raises:

Type Description
ValueError

If no element in the window equals value.

intersects

Accepted call forms
intersects(other: H3Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[H3Cell | None]) -> NDArray[bool_]
intersects(other: S2Cell | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[S2Cell | None]) -> NDArray[bool_]
intersects(other: GeohashCell | str | CellArray[GeohashCell | None]) -> NDArray[bool_]
intersects(other: Tile | (int | integer[Any] | ndarray[tuple[()], dtype[signedinteger[Any] | unsignedinteger[Any]]]) | str | CellArray[Tile | None]) -> NDArray[bool_]

Test whether every row hierarchically intersects the paired cell.

Two cells intersect when either is an ancestor of the other.

Parameters:

Name Type Description Default
other same-grid cell, integer id, string token, or CellArray

One identity broadcast to every row, or a same-length array.

required

Returns:

Type Description
ndarray

One read-only boolean per row.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> gm.CellArray([cell, neighbor]).intersects(cell).tolist()
[True, False]

parent

parent(depth: int | None = None, /) -> CellArray[CellT_co]

Parent cell of every input cell.

Parameters:

Name Type Description Default
depth int

Target depth; defaults to one coarser than each row.

None

Returns:

Type Description
CellArray

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> neighbor = list(cell.neighbors)[0]
>>> assert neighbor is not None
>>> cells = gm.CellArray([cell, neighbor])
>>> parent = cells.parent(6)[0]
>>> assert parent is not None
>>> parent.token
'86283082fffffff'

to_numpy

Accepted call forms
to_numpy() -> NDArray[object_]
to_numpy() -> NDArray[uint64]
to_numpy() -> NDArray[uint64] | NDArray[object_]

Return a read-only NumPy identity column.

H3, S2, and tile arrays expose their validated ids as uint64 (zero-copy for contiguous selections). Geohash has no public numeric id, so it returns an object array of typed GeohashCell values.

Returns:

Type Description
ndarray

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> type(gm.CellArray([cell]).to_numpy()).__name__
'ndarray'

to_polygon

to_polygon() -> Polygon | MultiPolygon

Dissolve this cell set into one WGS 84 longitude/latitude outline. Missing rows are skipped; this is a set operation over present cells. A seam-crossing outline preserves support on both sides of ±180° rather than filling through the map's middle; its chart-cut edge is not part of the represented cell boundary.

Returns:

Type Description
Polygon or MultiPolygon

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> gm.CellArray([cell]).to_polygon().geometry_type
'Polygon'

uncompact

uncompact(depth: int, /) -> CellArray[CellT]

Expand this cell set to a uniform depth, returned in the grid's public identity order. Expansion is bounded only by checked size arithmetic and allocator capacity; it has no fixed cell-count limit. Missing rows are skipped; this is a set operation over present cells.

Parameters:

Name Type Description Default
depth int

Target depth; no coarser than any row.

required

Returns:

Type Description
CellArray

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> len(gm.CellArray([cell]).uncompact(8))
7

value_counts

value_counts() -> tuple[CellArray[CellT], ndarray[tuple[int], dtype[int64]]]

Unique cells and counts, ordered by descending count (pandas value_counts parity), with first appearance breaking ties.

Returns:

Type Description
tuple

(unique_cells, counts) where unique_cells is a CellArray and counts is a read-only int64 ndarray.

Examples:

>>> import gometry as gm
>>> p = gm.Point(-122.4194, 37.7749, crs=4326)
>>> cell = gm.h3_cover(p, resolution=7)[0]
>>> assert cell is not None
>>> unique, counts = gm.CellArray([cell, cell]).value_counts()
>>> counts.tolist()
[2]