S2 单元函数

MatrixOne 提供了一系列 S2 单元函数,用于操作 Google S2 几何库的单元网格。这些函数将 POINT 几何值转换为以 BIGINT UNSIGNED 存储的 S2 CellID 值,并支持查询单元层级、计算父单元、获取单元中心点、以平方米为单位估算单元面积,以及检测邻接关系。S2 单元以分层四边形网格覆盖球面,级别从 0(粗粒度,一个立方体面)到 30(细粒度,边长约 1 厘米)。

Syntax

各 S2 单元函数遵循 S2_CellID*(...) 命名模式,接受 BIGINT UNSIGNED S2 CellID 值、POINT/POINT32 几何值以及可选的 INT 层级参数。具体语法参见下方各函数说明。

Arguments

所有 S2 单元函数均操作 BIGINT UNSIGNED S2 CellID 值和/或 POINT/POINT32 几何值。NULL 参数产生 NULL 结果。各函数的具体参数列表和返回类型请参见下方函数描述。

函数列表

函数

描述

S2_CellID(pt)

返回 POINT 几何值对应的 S2 CellID。

S2_CellID_Level(cell_id)

返回单元所在层级(0-30)。

S2_CellID_Parent(cell_id[, level])

返回指定层级的父单元 CellID。

S2_CellID_Center(cell_id)

返回单元的中心点,类型为 POINT。

S2_CellID_Area(cell_id)

返回单元的近似面积,单位为 m²。

S2_CellID_EdgeNeighbors(cell_id)

返回 4 个边相邻邻居 CellID 的 JSON 数组。

S2_CellID_AllNeighbors(cell_id)

返回全部 8 个邻居 CellID 的 JSON 数组。

S2_CellID_AreNeighbors(cell_a, cell_b)

若两单元为邻居关系返回 1,否则返回 0。

英式拼写别名 S2_CellID_EdgeNeighboursS2_CellID_AllNeighboursS2_CellID_AreNeighbours 同样被接受,行为完全一致。

S2_CellID()

语法

S2_CellID(pt)

参数说明

ptPOINTPOINT32 几何值。

返回值

POINT 输入返回 BIGINT UNSIGNEDPOINT32 输入返回 BIGINT UNSIGNED。返回的 CellID 始终为第 30 级的叶单元。若参数为 NULL 则返回 NULL

使用说明

  • 点的经度必须在 [-180, 180] 范围内,纬度必须在 [-90, 90] 范围内。

  • 空点(POINT EMPTY)会导致报错。

  • 该函数使用 S2 几何库将点的坐标转换为 S2 叶单元。

S2_CellID_Level()

语法

S2_CellID_Level(cell_id)

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 INT。层级为 0 到 30 之间的整数(含 0 和 30)。若参数为 NULL 则返回 NULL

使用说明

  • 传入 0 作为 CellID 会导致”invalid S2 CellId”错误。

S2_CellID_Parent()

语法

S2_CellID_Parent(cell_id[, level])

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。level:可选 INT,指定期望的父单元层级。若省略,默认值为 cell_level - 1(粗一个层级)。

返回值

返回 BIGINT UNSIGNED,为指定层级的父单元 CellID。若任一参数为 NULL 则返回 NULL

使用说明

  • 父单元层级必须在 [0, 30] 范围内。

  • 父单元层级必须比当前单元的层级更粗(数值上更小)。请求更细或相同层级会导致报错。

S2_CellID_Center()

语法

S2_CellID_Center(cell_id)

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 POINT,包含单元中心的经度和纬度。若参数为 NULL 则返回 NULL

S2_CellID_Area()

语法

S2_CellID_Area(cell_id)

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 DOUBLE,为单元的近似面积,单位为平方米(m²)。较粗(层级较低)的单元面积更大。若参数为 NULL 则返回 NULL

S2_CellID_EdgeNeighbors()

语法

S2_CellID_EdgeNeighbors(cell_id)

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 JSON 数组,包含恰好 4 个 BIGINT UNSIGNED 值,表示 4 个边相邻邻居的 CellID。若参数为 NULL 则返回 NULL

使用说明

  • 英式拼写别名 S2_CellID_EdgeNeighbours(cell_id) 返回相同结果。

S2_CellID_AllNeighbors()

语法

S2_CellID_AllNeighbors(cell_id)

参数说明

cell_id:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 JSON 数组,包含 8 个 BIGINT UNSIGNED 值,表示所有邻居 CellID(4 个边邻居加 4 个顶点邻居)。若参数为 NULL 则返回 NULL

使用说明

  • 英式拼写别名 S2_CellID_AllNeighbours(cell_id) 返回相同结果。

S2_CellID_AreNeighbors()

语法

S2_CellID_AreNeighbors(cell_a, cell_b)

参数说明

cell_acell_b:表示有效 S2 CellID 的 BIGINT UNSIGNED 值。

返回值

返回 TINYINT(1):若两单元为邻居关系(同层级下边相邻或顶点相邻)返回 1,否则返回 0。若任一参数为 NULL 则返回 NULL

使用说明

  • 一个单元不是自身的邻居(返回 0)。

  • 不同层级的单元不被视为邻居(返回 0)。

  • 该关系具有对称性:若 A 是 B 的邻居,则 B 也是 A 的邻居。

  • 英式拼写别名 S2_CellID_AreNeighbours(cell_a, cell_b) 返回相同结果。

错误场景

以下输入会导致运行时错误:

情形

错误消息

CellID 为 0

invalid input: invalid S2 CellId: 0

经度/纬度超出范围

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

空 POINT 几何值

invalid input: invalid point payload

父单元层级超出 [0, 30]

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

父单元层级比当前单元层级更细

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

示例

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;

另请参阅