Apache Spark SQL 地理空间数据类型详解:GEOMETRY 与 GEOGRAPHY 的选型、语法与实践
2026/9/19 23:51:10 网站建设 项目流程

Apache Spark SQL 地理空间数据类型详解:GEOMETRY 与 GEOGRAPHY 的选型、语法与实践

【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址: https://gitcode.com/gh_mirrors/sp/spark

本文全面解析 Apache Spark SQL 中基于 OGC Simple Feature Access 规范的GEOMETRY(平面笛卡尔坐标系)与GEOGRAPHY(经纬度地理坐标系)两大地理空间数据类型。你将从本文掌握:两类类型的适用场景与选型原则、带 SRID 的类型声明语法、基于 WKB 的值构造与校验规则、内置 ST 函数的用法、SRID 注册表与存储兼容性(Parquet/Delta/Iceberg),并结合当前仓库源码理解其底层实现与错误处理机制。

一、GEOMETRY 与 GEOGRAPHY:两套坐标体系,两种计算模型

Spark SQL 支持GEOMETRYGEOGRAPHY两种地理空间类型,其定义遵循 Open Geospatial Consortium (OGC) Simple Feature Access 规范。运行时,值以Well-Known Binary (WKB)二进制形式表示,并关联一个Spatial Reference Identifier (SRID)以确定其坐标系统;值最终如何持久化则由具体数据源决定。

类型坐标系典型用途与说明
GEOMETRYCartesian(平面)投影坐标或局部坐标下的平面计算。以平面坐标系表示点、线、多边形,适合 Web Mercator(SRID 3857)、UTM 或局部网格(如工程/CAD)。可接受注册表中任意 SRID,包括 SRID 0(未指定 CRS)。
GEOGRAPHYGeographic(经纬度)基于地球的数据;在球面/椭球面上计算距离与面积。坐标以经度、纬度(度)表示。边插值始终为SPHERICAL(球形)。仅接受地理类 SRID,最常见为 4326(WGS 84)。

在 SQL 中,GEOMETRYGEOGRAPHY列必须显式声明 SRID(或ANY)。当通过ST_GeomFromWKB(wkb)(不带显式 SRID)构造值时,该值 SRID 为0(未指定);而ST_GeogFromWKB(wkb)始终返回 SRID 为 4326 的值。

何时选 GEOMETRY

  • 数据为局部或投影坐标(例如以米为单位的工程/CAD 数据,或 Web Mercator 地图瓦片);
  • 需要在小范围/区域级进行平面运算:求交、并集、裁剪、包含关系、叠加分析,且把地表当作平面处理误差可接受;
  • 顶点间距近或范围足够小,地球曲率可忽略不计。

何时选 GEOGRAPHY

  • 数据为全球范围或跨越较大范围(例如国界、全球兴趣点);
  • 距离或面积的计算必须尊重地球曲率(例如两城市间最短路径、球面上多边形面积);
  • 典型场景包括航空、航海、全球移动出行等依赖大圆(great-circle)或测地线(geodesic)行为的使用场景。

选错类型可能得到误导性结果:例如球面上伦敦到纽约的最短路径会穿越加拿大,而平面 GEOMETRY 计算可能给出不经过加拿大的路径。

二、SQL 类型语法:SRID 与 ANY

在 SQL 中,类型必须携带 SRID 或ANY

  • 固定 SRID(列内所有值共享同一个 SRID):
    • GEOMETRY(srid)—— 例如GEOMETRY(4326)GEOMETRY(3857)
    • GEOGRAPHY(srid)—— 例如GEOGRAPHY(4326)
  • 混合 SRID(列内各值可以有不同的 SRID):
    • GEOMETRY(ANY)
    • GEOGRAPHY(ANY)

不带参数、未声明(srid)(ANY)的裸GEOMETRY/GEOGRAPHY在 SQL 中不受支持。

从当前仓库源码看,类型层面对「固定/混合」的区分做了显式建模:GeometryType.scala 中isMixedSrid通过判断 SRID 是否为MIXED_SRID实现;typeName在混合 SRID 时显示为"geometry(any)",固定 SRID 时显示为"geometry($srid)"。此外,acceptsGeometryType规定了类型兼容规则:混合 SRID 类型可接受任意其他 GeometryType(固定或混合);固定 SRID 类型只接受同 SRID 的 GeometryType,且不接受混合 SRID。

三、创建带地理空间列的表格

-- 固定 SRID:所有值必须使用给定 SRID(例如 WGS 84) CREATE TABLE points ( id BIGINT, pt GEOMETRY(4326) ); CREATE TABLE locations ( id BIGINT, loc GEOGRAPHY(4326) ); -- 混合 SRID:每一行可以有不同 SRID CREATE TABLE mixed_geoms ( id BIGINT, geom GEOMETRY(ANY) );

四、构造 GEOMETRY 与 GEOGRAPHY 值

值通过内置函数从Well-Known Binary (WKB)构造。WKB 是点、线、多边形等空间形状的标准二进制编码格式。

从 WKB(二进制)构造:

  • ST_GeomFromWKB(wkb)—— 返回 SRID 默认值为 0 的 GEOMETRY
  • ST_GeomFromWKB(wkb, srid)—— 返回给定 SRID 的 GEOMETRY
  • ST_GeogFromWKB(wkb)—— 返回 SRID 为 4326 的 GEOGRAPHY

示例(WKB 格式的点,随后写入表):

-- Point (1, 2) 的 WKB(小端序、2D 点) SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040'); SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 4326); SELECT ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040'); INSERT INTO points (id, pt) VALUES (1, ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 4326));

在源码层面,stExpressions.scala 中ST_GeomFromWKBST_GeogFromWKBST_AsBinaryST_SridST_SetSrid均实现为RuntimeReplaceable表达式:ST_GeomFromWKB(wkb)ST_GeogFromWKB(wkb)是一元表达式、输入类型为BinaryTypeST_GeomFromWKB(wkb, srid)ST_SetSrid(geo, srid)为二元表达式;ST_SridST_AsBinary的输入限定为GeographyType/GeometryType集合。这意味着这些 ST 函数在 SQL 中可直接与二进制字面量(X'...')组合使用,并被规约到统一的内部实现。

五、WKB 坐标处理规则

解析 WKB 时 Spark 应用以下规则,违反任一规则都会产生解析错误:

  • 空点:对于Point几何(包括 MultiPoint 内部的点),允许NaN(Not a Number)坐标值,表示一个空点(如 Well-Known Text 中的POINT EMPTY)。LineStringPolygon(及其内部的点)不允许坐标值为 NaN。
  • 非点坐标LineStringPolygon环以及属于这些结构的点,坐标值必须为有限值(不允许 NaN、正负无穷)。
  • 无穷大:任何坐标值都不接受正无穷或负无穷
  • 多边形环:每个环必须闭合(首尾点相等)且至少 4 个点LineString至少 2 个点。
  • GEOGRAPHY 边界:当 WKB 以GEOGRAPHY解析(例如通过ST_GeogFromWKB)时,经度必须在[-180, 180](含端点)、纬度必须在[-90, 90](含端点)。GEOMETRY 不强制这些边界。
  • 无效 WKB:Null 或空输入、字节被截断、无效的几何类型或字节序,以及其他格式错误的 WKB。

六、内置地理空间(ST)函数

Spark SQL 提供一组标量函数处理 GEOMETRY 与 GEOGRAPHY 值。完整函数列表及详细参数说明和示例,见 Built-in Functions 页面的Geospatial ST Functions一节。当前版本提供的函数汇总如下:

函数说明
ST_AsBinary(geo[, endianness])将 GEOMETRY 或 GEOGRAPHY 值以 WKB(BINARY)形式返回。可选endianness参数为'NDR'(小端,默认)或'XDR'(大端)。
ST_GeomFromWKB(wkb[, srid])解析 WKB 并返回 GEOMETRY。可选srid参数设置 SRID;省略时 SRID 为0
ST_GeogFromWKB(wkb)解析 WKB 并返回 SRID 为 4326 的 GEOGRAPHY。
ST_Srid(geo)返回 GEOMETRY 或 GEOGRAPHY 值的 SRID(输入为 NULL 时返回 NULL)。
ST_SetSrid(geo, srid)返回带给定 SRID 的新 GEOMETRY 或 GEOGRAPHY。新 SRID 必须对该值类型有效。

关于字节序,源码注释明确:ST_AsBinary未指定字节序时默认使用小端编码("NDR")——这正是上面 WKB 示例(X'01010000...',小端点)能直接读写回环的原因。

示例:

SELECT hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040'))); -- 0101000000000000000000F03F0000000000000040 SELECT hex(ST_AsBinary(ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040'), 'XDR')); -- 00000000013FF00000000000004000000000000000 SELECT ST_Srid(ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040')); -- 4326 SELECT ST_Srid(ST_SetSrid(ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040'), 3857)); -- 3857

这些函数均在仓库测试中得到了验证,例如 st-functions.sql.out 中记录了ST_GeomFromWKBST_SetSridST_GeogFromWKB以及CAST(... AS GEOMETRY(4326))等查询的分析结果,可直接作为复现参考。

七、SRID 与存储值

  • 固定 SRID 列:列内每个值的 SRID 必须与列类型一致。插入不同 SRID 的值会抛出GEO_ENCODER_SRID_MISMATCH_ERROR。可用ST_SetSrid将值的 SRID 改为与列匹配。
  • 混合 SRID 列GEOMETRY(ANY)GEOGRAPHY(ANY)):每行值可以有不同的 SRID。每个值仍须对该类型有效;无效 SRID 会抛出ST_INVALID_SRID_VALUE
  • 存储:Parquet、Delta、Iceberg 以「每列固定 SRID」方式存储 geometry/geography。它们不支持持久化GEOMETRY(ANY)GEOGRAPHY(ANY);混合 SRID 类型仅用于内存/查询场景。

源码对上述错误与兼容性做了精确实现:

  • GeometryType.scala 的assertSridAllowedForType方法:固定 SRID 类型下值 SRID 与列 SRID 不一致时抛出GEO_ENCODER_SRID_MISMATCH_ERROR(错误参数包含 type、valueSrid、typeSrid);混合 SRID 类型下值 SRID 不在支持范围内时抛出ST_INVALID_SRID_VALUE
  • 同一文件中的GeometryType.apply(srid)在构造类型时即通过CartesianSpatialReferenceSystemMapper查注册表,查不到即抛ST_INVALID_SRID_VALUEtoSrid对无法识别的 CRS 字符串抛ST_INVALID_CRS_VALUE
  • ST_INVALID_SRID_VALUE错误类在 QueryExecutionErrors.scala 中统一定义,并附带发生错误的 srid 参数。
  • 测试层面,GeometryDataFrameSuite.scala 与 GeographyDataFrameSuite.scala 分别对GEO_ENCODER_SRID_MISMATCH_ERRORST_INVALID_SRID_VALUE做了断言,是理解「列 SRID 与值 SRID 校验」行为的最直接参考。

另外从源码可以确认类型的一些实现细节:GeometryTypedefaultSize为 2048 字节(约可存储 120 个 2D 点);其 JSON 表示使用 CRS 字符串(如geometry(OGC:CRS84)),这与 Parquet、Delta、Iceberg 的存储规范保持一致;类型相等性由 SRID 唯一决定(equals/hashCode基于 SRID)。

八、支持的 SRID

Spark 内置一份预构建的 SRID 注册表,由 PROJ 数据库中的坐标系与 OGC 标准覆盖项组合而成,用于坐标系的校验与正确处理。

SRID 兼容规则:

  • GEOMETRY接受注册表中的所有 SRID(地理 + 投影 + SRID 0)
  • GEOGRAPHY仅接受地理类 SRID(经纬度坐标系)

PROJ 版本与 Spark 版本对照

Spark 版本PROJ 版本
4.2.09.8.1

SRID 注册表固定绑定上表中的 PROJ 版本,不会与外部数据库实时同步。

OGC 标准覆盖项

Spark 对 PROJ 数据库中的部分 SRID 应用了 OGC 标准覆盖:

SRIDPROJ CRS 标识OGC CRS 标识说明
4326EPSG:4326OGC:CRS84WGS 84(按 OGC 标准为经度/纬度顺序)
4267EPSG:4267OGC:CRS27NAD27
4269EPSG:4269OGC:CRS83NAD83

常用 SRID

SRIDCRS 标识名称CRS 类型说明
0SRID:0UnspecifiedCartesian未定义 CRS 的坐标(ST_GeomFromWKB(wkb)的默认值)
4326OGC:CRS84WGS 84Geographic1984 世界大地坐标系(经度/纬度),GPS 坐标、全球数据(GEOGRAPHY 默认)
4267OGC:CRS27NAD27Geographic北美 1927 大地基准
4269OGC:CRS83NAD83Geographic北美 1983 大地基准
3857EPSG:3857Web MercatorProjectedWeb 地图服务使用的伪墨卡托投影

注意事项:

  • GEOMETRY(0)表示固定 SRID 为 0。若需要每行不同 SRID,请使用GEOMETRY(ANY)
  • Parquet 与 Iceberg 的地理空间规范要求每列固定 SRID,因此不支持持久化GEOMETRY(ANY)GEOGRAPHY(ANY)

SRID 校验示例

无效 SRID(不在注册表中):

SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 99999); -- Throws [ST_INVALID_SRID_VALUE]

投影 SRID 用于 GEOGRAPHY 类型:

CREATE TABLE invalid_geo (id BIGINT, loc GEOGRAPHY(3857)); -- Throws [ST_INVALID_SRID_VALUE] (3857 is projected, not geographic)

九、数据类型的 API 参考

关于 Scala、Java、Python、SQL 中完整的数据类型列表与 API 用法,参见 Data Types 文档;全部 ST 函数的签名、参数与示例参见 Built-in Functions 中 Geospatial ST Functions 章节。本文所涉及的 Geo 类型与函数实现集中位于 sql/api 的 types 包 与 catalyst 的 expressions/st 包,测试与示例可进一步参阅 GeometryDataFrameSuite.scala、GeographyDataFrameSuite.scala 及 st-functions.sql.out。

【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址: https://gitcode.com/gh_mirrors/sp/spark

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询