跳到主要内容

GEO类型文档

地理空间类型是数据库中用于存储和操作地理空间数据的特殊数据类型,可表示点、线、面等几何对象。

  • 核心用途:
    • 存储地理位置信息(如经纬度)。
    • 支持空间查询(如距离计算、区域包含、相交判断)。
    • 处理地理空间分析(如缓冲区分析、路径规划)。 地理信息系统在地图服务、物流调度、位置社交、气象监测等领域有广泛应用,核心需求是高效存储海量空间数据并支持低延迟的空间计算。

核心编码技术

S2 Geometry 库

S2 Geometry 是由 Google 开发的球面几何编码系统,核心思想是通过球面到平面的映射实现全球地理空间的高效索引。 核心原理:

  • 球面映射:将地球球面投影到正六面体的 6 个面上,将三维球面数据转换为二维平面数据。
  • 层级网格划分:每个面被递归划分为四边形网格(cell),每个 cell 可进一步细分为 4 个更小的子 cell,形成 30 级精度的层级结构(级别越高,cell 面积越小,精度越高)。
  • 64 位编码:每个 cell 被分配一个唯一的 64 位 ID,通过 ID 可快速定位空间位置并判断空间关系。
  • Hilbert 曲线排序:采用 Hilbert 空间填充曲线对 cell 进行编码,使空间上相邻的 cell 具有连续的 ID,优化范围查询性能。 优势:
  • 高精度与平滑过渡:30 级层级划分,精度从全球范围(级别 0)到厘米级(级别 30),过渡平滑,满足不同场景需求。
  • 全球范围查询效率:适合大尺度空间查询(如跨洲、跨国区域分析),无明显性能衰减。
  • 空间关系计算高效:通过 cell ID 可快速判断包含、相交等关系,避免复杂的几何运算。

GeoHash 编码

GeoHash 是一种基于正轴等角圆柱投影的地理编码方式,通过将经纬度转换为字符串实现空间索引。 核心原理:

  • 平面投影:将地球球面近似为平面,通过经度和纬度的二分法递归划分区域。
  • 矩形网格划分:将地球表面划分为不同精度的矩形 cell,字符串长度决定精度(最长 12 位),长度每增加 1 位,精度约提升 10 倍。
  • Z 阶曲线编码:通过交替截取经纬度的二进制位,形成 Z 阶曲线(Z-order curve),将二维坐标转换为一维字符串。 特点:
  • 索引便捷性:通过字符串前缀匹配可快速查询相邻区域(如前缀相同的 GeoHash 编码对应空间上邻近的区域)。
  • 局限性:
    • 精度层级有限:最多 12 级,层级过渡较陡峭,难以满足高精度平滑划分需求。
    • Z 阶曲线突变性:空间上相邻的区域可能因曲线跳跃导致编码不连续,影响范围查询准确性。
    • 大尺度查询效率低:全球范围查询时,需扫描大量离散 cell,性能较差。

综合对比 S2 Geometry 和 GeoHash 的特性,我们选择 S2 Geometry 库作为地理空间处理的第三方依赖,主要原因如下:

  • 全球范围查询适配性:S2 的层级网格设计更适合大尺度空间分析,而 GeoHash 在跨区域查询时存在性能瓶颈。
  • 精度与平滑性:S2 的 30 级层级划分可实现从全球到厘米级的平滑过渡,满足多场景精度需求,优于 GeoHash 的 12 级划分。
  • 空间连续性:Hilbert 曲线相比 Z 阶曲线的空间连续性更好,可减少范围查询中的冗余计算。

WKT介绍

WKT(Well-Known Text) 是一种用于表示地理空间数据的标准的文本格式。

定义

  • 文本格式:用文本字符串描述几何对象的结构和坐标。
  • 特点:人类可读、易于编辑,适合手动输入或简单数据交换。

语法结构

  • 基本格式:几何类型(坐标值)
  • 常见几何类型:
    • 点(Point):POINT(经度,纬度) 例如:POINT(112.46,45.23) 表示某点的经纬度。
    • 线(LineString):LINESTRING(点1,点2) 例如:LINESTRING(0 0,1 1) 表示连接二个点的线段。
    • 多边形(Polygon):POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))

WKB介绍

WKB(Well-Known Binary) 是一种用于表示地理空间数据的标准的二进制数据格式。

定义

  • 二进制格式:用二进制编码表示几何对象,比 WKT 更紧凑、高效。
  • 特点:计算机内部存储和传输更优,节省空间,解析速度快。

编码结构

WKB 由以下部分组成:

  • 字节序(1 字节):
    • 0x00:大端序(Big Endian,网络字节序)
    • 0x01:小端序(Little Endian,常见于 Intel/AMD 架构)
  • 几何类型(4 字节整数):
    • 1:点(Point)
    • 2:线(LineString)
    • 3:多边形(Polygon)
    • ...(其他类型)
  • 坐标值:
    • 点:x,y(或 x, y, z )
    • 线:点1坐标,点2坐标
    • 多边形:点1坐标,点2坐标..... 示例
    01 01 00 00 00 00 00 00 00 00 F0 3F 00 00 00 00 00 00 00 40
└─┘ └─┘ └───────────────┘ └───────────────┘
│ │ │ │
小端 点类型 x=1.0 y=2.0

GeoPoint类型

1.利用String类型或者Varchar类型存储wkt格式的文本进行存储

CREATE TABLE simple_point (  id INT,  wkt STRING) ;
INSERT INTO simple_point VALUES(1,'POINT(121.4737 31.2304)');

create table simple_point(id int, wkt VARCHAR(255);
INSERT INTO simple_point VALUES(1,'POINT(121.4737 31.2304)');

用wkt格式存储的geo类型查询

select st_astext(st_geometryfromtext(wkt)) from simple_point;
+-------------------------------------+
| st_astext(st_geometryfromtext(wkt)) |
+-------------------------------------+
| POINT (121.4737 31.2304) |
+-------------------------------------+

2.利用wkb格式进行存储

CREATE TABLE simple_point (  id INT,  wkb STRING) ;
INSERT INTO simple_point VALUES(1,'\x01010000005f07ce19515e5e4097ff907efb3a3f40');

create table simple_point(id int, wkb VARCHAR(255);
INSERT INTO simple_point VALUES(1,'\x01010000005f07ce19515e5e4097ff907efb3a3f40');

用wkb格式存储的geo类型进行查询

select st_astext(st_geometryfromwkb(wkb)) from simple_point;
+------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+------------------------------------+
| POINT (121.4737 31.2304) |
+------------------------------------+

3.利用两个浮点数存储整个坐标,x位纬度,y为经度

CREATE TABLE simple_point_double (id INT,x DOUBLE,y DOUBLE) 
INSERT INTO simple_point_double VALUES(0,1,2);

用浮点数存储的geo类型进行查询

select st_astext(st_point(x,y)) from simple_point_double;
+--------------------------+
| st_astext(st_point(x,y)) |
+--------------------------+
| POINT (1 2) |
+--------------------------+

GeoLine类型

利用String类型或者Varchar类型存储wkt格式的文本进行存储

CREATE TABLE simple_line (  id INT,  wkt STRING)
INSERT INTO simple_line VALUES(1,'LINESTRING(116.4074 39.9042, 121.4737 31.2304)');

CREATE TABLE simple_line ( id INT, wkt VARCHAR(255)
INSERT INTO simple_line VALUES(1,'LINESTRING(116.4074 39.9042, 121.4737 31.2304)');

使用wkt存储的geo类型进行查询

select st_astext(st_linefromtext(wkt)) from simple_line;
+-------------------------------------------------+
| st_astext(st_linefromtext(wkt)) |
+-------------------------------------------------+
| LINESTRING (116.4074 39.9042, 121.4737 31.2304) |
+-------------------------------------------------+

2.利用wkb格式进行存储

CREATE TABLE simple_line (  id INT,  wkb STRING)
INSERT INTO simple_line VALUES(1,'\x010200000002000000fc1873d7121a5d4088855ad3bcf343405f07ce19515e5e4097ff907efb3a3f40');

CREATE TABLE simple_line ( id INT, wkb VARCHAR(255)
INSERT INTO simple_line VALUES(1,'\x010200000002000000fc1873d7121a5d4088855ad3bcf343405f07ce19515e5e4097ff907efb3a3f40');

利用wkb格式存储的geo类型进行查询

select st_astext(st_geometryfromwkb(wkb)) from simple_line;
+-------------------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+-------------------------------------------------+
| LINESTRING (116.4074 39.9042, 121.4737 31.2304) |
+-------------------------------------------------+

GeoPolygon类型

1.利用String类型或者Varchar类型存储wkt格式的文本进行存储

CREATE TABLE simple_polygon (  id INT,  wkt STRING)
INSERT INTO simple_polygon VALUES(1,'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))');

CREATE TABLE simple_polygon ( id INT, wkt VARCHAR(255)
INSERT INTO simple_polygon VALUES(1,'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))');
select st_astext(st_polygon(wkt)) from simple_polygon;
+------------------------------------------+
| st_astext(st_polygon(wkt)) |
+------------------------------------------+
| POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0)) |
+------------------------------------------+

2.利用wkb格式进行存储

CREATE TABLE simple_polygon_wkb (  id INT,  wkb STRING)
INSERT INTO simple_polygon_wkb VALUES(1,'\x010300000001000000050000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000024400000000000000000');

CREATE TABLE simple_polygon_wkb ( id INT, wkb VARCHAR(255)
INSERT INTO simple_polygon_wkb VALUES(1,'\x010300000001000000050000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000024400000000000000000');

利用wkb格式进行查询

select st_astext(st_geometryfromwkb(wkb)) from simple_polygon_wkb;
+------------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+------------------------------------------+
| POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0)) |
+------------------------------------------+

GeoMultiPolygon类型

1.利用String类型或者Varchar类型存储wkt格式的文本进行存储

CREATE TABLE simple_multipolygon (  id INT,  wkt STRING)
INSERT INTO simple_multipolygon VALUES(1,'MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)),((20 20, 20 30, 30 30, 30 20, 20 20)))');

CREATE TABLE simple_multipolygon ( id INT, wkt VARCHAR(255)
INSERT INTO simple_multipolygon VALUES(1,'MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), -- 第一个多边形((20 20, 20 30, 30 30, 30 20, 20 20)) -- 第二个多边形)');

利用wkt格式进行查询

select st_astext(st_geometryfromtext(wkt)) from simple_multipolygon;
+----------------------------------------------------------------------------------------+
| st_astext(st_geometryfromtext(wkt)) |
+----------------------------------------------------------------------------------------+
| MULTIPOLYGON (((10 0, 10 10, 0 10, 0 0, 10 0)), ((30 20, 30 30, 20 30, 20 20, 30 20))) |
+----------------------------------------------------------------------------------------+

GeoMultiPolygon的wkb格式转换还暂不支持

GeoCircle类型

利用三个浮点数分别存储圆的中心坐标x,y和圆的半径(因为circle并不符合wkb与wkt格式,所以只能这样存储)

create table simple_circle(id int, X double,Y double, R double)
INSERT INTO simple_circle VALUES(1,1.0,1.0,2);

利用存储的三个坐标进行查询

select st_astext(st_circle(X,Y,R)) from simple_circle;
+-----------------------------+
| st_astext(st_circle(X,Y,R)) |
+-----------------------------+
| CIRCLE ((1 1), 2) |
+-----------------------------+

Doris中对Geo类型的约束

索引

因为doris并未直接实现Geo这种类型,而是用wkt,wkb来存储和转换,所以并不能通过索引技术对GEO类型的查询进行加速。 精度

在wkt转化为GEO输出时,只能保证13位精准度

mysql> SELECT ST_AsText(ST_GeometryFromText("POINT (1 3.1415926535897223)"));
+----------------------------------------------------------------+
| ST_AsText(ST_GeometryFromText("POINT (1 3.1415926535897223)")) |
+----------------------------------------------------------------+
| POINT (1 3.14159265358972) |
+----------------------------------------------------------------+

在二进制转化为GEO输出,只能保证13位精准度

mysql> select ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_Point(24.7,3.141592653589793))));
+--------------------------------------------------------------------------+
| ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_Point(24.7,3.141592653589793)))) |
+--------------------------------------------------------------------------+
| POINT (24.7 3.1415926535898) |
+--------------------------------------------------------------------------+

Geo类型常见的使用用途和方式

计算地球上两点之间的距离

计算北京到上海的距离,北京经度和纬度是(116.4074, 39.9042),上海的经度和纬度是(121.4737, 31.2304),可以通过下面这个函数来计算两个地方之间的距离。

select ST_DISTANCE_SPHERE(116.4074, 39.9042, 121.4737, 31.2304);
+----------------------------------------------------------+
| ST_DISTANCE_SPHERE(116.4074, 39.9042, 121.4737, 31.2304) |
+----------------------------------------------------------+
| 1067311.8461903075 |
+----------------------------------------------------------+

alt text

计算北京到纽约的距离,北京经度和纬度是(116.4074, 39.9042),纽约的经度和纬度是(-74.0060, 40.7128),通过下面的sql计算两地的距离

select ST_DISTANCE_SPHERE(116.4074, 39.9042, -74.0060, 40.7128);
+----------------------------------------------------------+
| ST_DISTANCE_SPHERE(116.4074, 39.9042, -74.0060, 40.7128) |
+----------------------------------------------------------+
| 10989107.361809434 |
+----------------------------------------------------------+

alt text

计算地球球面上的一定区域面积

大概计算纽约面积,多边形大概可以概括纽约整个面积

SELECT ST_AREA_SQUARE_KM(
ST_GeomFromText('POLYGON((
-74.2591 40.9155,
-73.8726 40.9147,
-73.7004 40.7506,
-73.9442 40.5840,
-74.0817 40.6437,
-74.1502 40.6110,
-74.0984 40.6550,
-74.0431 40.7290,
-74.0136 40.7903,
-73.9352 40.8448,
-74.2591 40.9155
))'));

+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ST_AREA_SQUARE_KM( ST_GeomFromText('POLYGON((-74.2591 40.9155, -73.8726 40.9147, -73.7004 40.7506, -73.9442 40.5840, -74.0817 40.6437,-74.1502 40.6110,-74.0984 40.6550,-74.0431 40.7290,-74.0136 40.7903, -73.9352 40.8448, -74.2591 40.9155))' )) |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 744.3806189617659 |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

alt text