S2 Cell Functions

MatrixOne provides a family of S2 cell functions for working with Google S2 geometry library cells. These functions convert POINT geometries into S2 CellIDs stored as BIGINT UNSIGNED values and support operations such as querying cell level, computing parent cells, retrieving cell centers, calculating approximate cell area in square meters, and testing neighbor relationships. S2 cells cover the sphere with a hierarchical quadrilateral grid at levels 0 (coarse, one face of the cube) through 30 (fine, approximately 1 cm on a side).

Syntax

Each S2 cell function follows the naming pattern S2_CellID*(...) and accepts specific argument types as documented in the per-function sections below. Arguments include BIGINT UNSIGNED S2 CellID values, POINT/POINT32 geometry values, and optional INT level parameters.

Arguments

All S2 cell functions operate on BIGINT UNSIGNED S2 CellID values and/or POINT/POINT32 geometry values. NULL arguments produce NULL results. See the individual function descriptions below for the exact argument list and return type of each function.

Function List

Function

Description

S2_CellID(pt)

Returns the S2 CellID for a POINT geometry.

S2_CellID_Level(cell_id)

Returns the level (0-30) of the cell.

S2_CellID_Parent(cell_id[, level])

Returns the parent cell at the given level.

S2_CellID_Center(cell_id)

Returns the center point of the cell as a POINT.

S2_CellID_Area(cell_id)

Returns the approximate area of the cell in m².

S2_CellID_EdgeNeighbors(cell_id)

Returns a JSON array of the 4 edge-adjacent neighbor CellIDs.

S2_CellID_AllNeighbors(cell_id)

Returns a JSON array of all 8 neighbor CellIDs.

S2_CellID_AreNeighbors(cell_a, cell_b)

Returns 1 if the cells are neighbors, 0 otherwise.

The British-spelling aliases S2_CellID_EdgeNeighbours, S2_CellID_AllNeighbours, and S2_CellID_AreNeighbours are also accepted and behave identically.

S2_CellID()

Syntax

S2_CellID(pt)

Arguments

pt: A POINT or POINT32 geometry value.

Return Value

Returns BIGINT UNSIGNED for POINT inputs and BIGINT UNSIGNED for POINT32 inputs. The returned CellID is always a leaf cell at level 30. Returns NULL if the argument is NULL.

Usage Notes

  • The point must have longitude in [-180, 180] and latitude in [-90, 90].

  • An empty point (POINT EMPTY) raises an error.

  • The function converts the point’s coordinates into an S2 leaf cell using the S2 geometry library.

S2_CellID_Level()

Syntax

S2_CellID_Level(cell_id)

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID.

Return Value

Returns INT. The level is an integer between 0 and 30 inclusive. Returns NULL if the argument is NULL.

Usage Notes

  • Passing 0 as the CellID raises an “invalid S2 CellId” error.

S2_CellID_Parent()

Syntax

S2_CellID_Parent(cell_id[, level])

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID. level: An optional INT specifying the desired parent level. If omitted, defaults to cell_level - 1 (one level coarser).

Return Value

Returns BIGINT UNSIGNED. The parent CellID at the requested level. Returns NULL if any argument is NULL.

Usage Notes

  • The parent level must be in the range [0, 30].

  • The parent level must be coarser than (numerically less than) the current cell’s level. Requesting a finer or equal level raises an error.

S2_CellID_Center()

Syntax

S2_CellID_Center(cell_id)

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID.

Return Value

Returns POINT containing the longitude and latitude of the cell’s center. Returns NULL if the argument is NULL.

S2_CellID_Area()

Syntax

S2_CellID_Area(cell_id)

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID.

Return Value

Returns DOUBLE. The approximate area of the cell in square meters (m²). Coarser (lower level) cells have larger areas. Returns NULL if the argument is NULL.

S2_CellID_EdgeNeighbors()

Syntax

S2_CellID_EdgeNeighbors(cell_id)

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID.

Return Value

Returns JSON array of exactly 4 BIGINT UNSIGNED values, representing the CellIDs of the 4 edge-adjacent neighbors. Returns NULL if the argument is NULL.

Usage Notes

  • The British-spelling alias S2_CellID_EdgeNeighbours(cell_id) returns the same result.

S2_CellID_AllNeighbors()

Syntax

S2_CellID_AllNeighbors(cell_id)

Arguments

cell_id: A BIGINT UNSIGNED value representing a valid S2 CellID.

Return Value

Returns JSON array of 8 BIGINT UNSIGNED values, representing all neighbor CellIDs (4 edge neighbors plus 4 vertex neighbors). Returns NULL if the argument is NULL.

Usage Notes

  • The British-spelling alias S2_CellID_AllNeighbours(cell_id) returns the same result.

S2_CellID_AreNeighbors()

Syntax

S2_CellID_AreNeighbors(cell_a, cell_b)

Arguments

cell_a, cell_b: BIGINT UNSIGNED values representing valid S2 CellIDs.

Return Value

Returns TINYINT(1): 1 if the two cells are neighbors (either edge-adjacent or vertex-adjacent at the same level), 0 otherwise. Returns NULL if either argument is NULL.

Usage Notes

  • A cell is NOT a neighbor of itself (returns 0).

  • Cells at different levels are NOT considered neighbors (returns 0).

  • The relationship is symmetric: if A is a neighbor of B, then B is a neighbor of A.

  • The British-spelling alias S2_CellID_AreNeighbours(cell_a, cell_b) returns the same result.

Error Cases

The following inputs cause runtime errors:

Condition

Error message

CellID is 0

invalid input: invalid S2 CellId: 0

Longitude/latitude out of range

invalid input: longitude/latitude out of range: (lng, lat)

Empty POINT geometry

invalid input: invalid point payload

Parent level outside [0, 30]

invalid input: S2 level must be between 0 and 30, got N

Parent level finer than cell level

invalid input: parent level N is finer than cell level M

Examples

DROP DATABASE IF EXISTS dbgeocell;
CREATE DATABASE dbgeocell;
USE dbgeocell;

SELECT S2_CellID_Level(S2_CellID(ST_GeomFromText('POINT(116.3975 39.9087)'))) AS leaf_level;

SELECT S2_CellID_Level(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(116.3975 39.9087)')), 10)) AS parent_level;

SELECT ST_AsText(S2_CellID_Center(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 5))) AS center_point;

SELECT S2_CellID_Area(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 5)) > S2_CellID_Area(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10)) AS coarser_is_larger;

SELECT JSON_LENGTH(S2_CellID_EdgeNeighbors(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10))) AS n_edge;

SELECT JSON_LENGTH(S2_CellID_AllNeighbors(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10))) AS n_all;

SELECT S2_CellID_AreNeighbors(
    S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10),
    S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10)) AS self_not_neighbor;

SELECT S2_CellID_AreNeighbors(
    c,
    CAST(JSON_UNQUOTE(JSON_EXTRACT(S2_CellID_EdgeNeighbors(c), '$[0]')) AS UNSIGNED)) AS edge_is_neighbor
FROM (SELECT S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10) AS c) t;

SELECT S2_CellID_AreNeighbors(
    S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 10),
    S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 5)) AS cross_level;

CREATE TABLE s2t(id INT, pt POINT, cell BIGINT UNSIGNED);
INSERT INTO s2t VALUES (1, ST_GeomFromText('POINT(116.3975 39.9087)'), 0);
INSERT INTO s2t VALUES (2, ST_GeomFromText('POINT(121.4737 31.2304)'), 0);
UPDATE s2t SET cell = S2_CellID(pt);
SELECT id, S2_CellID_Level(cell) AS lvl FROM s2t ORDER BY id;
DROP TABLE IF EXISTS s2t;

SELECT S2_CellID(NULL) AS a, S2_CellID_Level(NULL) AS b, S2_CellID_Center(NULL) AS c;

-- Expected-Success: false
SELECT S2_CellID_Level(0);

-- Expected-Success: false
SELECT S2_CellID(ST_GeomFromText('POINT(200 100)'));

-- Expected-Success: false
SELECT S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 40);

-- Expected-Success: false
SELECT S2_CellID_Parent(S2_CellID_Parent(S2_CellID(ST_GeomFromText('POINT(0 0)')), 5), 20);

DROP DATABASE dbgeocell;

See Also