
TDengine 地理信息使用说明
概述
TDengine 从 3.x 版本开始支持地理信息(GIS)功能,提供了一系列 ST_ 开头的地理信息函数,这些函数基于 GEOS 库实现,支持 WKT(Well-Known Text)格式的地理数据处理。
同时 TDengine 也支持地理信息数据类型,这里一起举例说明下如何使用?
重要说明
数据写入和查询的自动转换
- 写入数据时 :可直接使用 WKT 字符串,无需调用
ST_GeomFromText函数 - 查询数据时 :返回结果自动为 WKT 格式,无需调用
ST_AsText函数
TDengine 会自动处理 WKT 字符串与内部 GEOMETRY 类型之间的转换。
函数使用限制
⚠️ 重要限制:
- WHERE 子句限制:TDengine 目前不支持在 WHERE 子句中使用地理函数进行过滤
- 参数类型限制 :向地理函数传递 WKT 参数时,必须使用
ST_GeomFromText()函数将文本转换为 GEOMETRY 类型
基本用法示例
1. 创建表与插入数据
sql
-- 创建包含地理信息列的表
CREATE TABLE geo_points (
ts TIMESTAMP,
name VARCHAR(64),
geom GEOMETRY(256)
);
-- 直接使用 WKT 字符串插入数据(无需 ST_GeomFromText)
INSERT INTO geo_points VALUES
(NOW, 'tiananmen', 'POINT(116.397 39.908)'),
(NOW, 'airport', 'POINT(116.407 40.080)');
-- 插入线串
INSERT INTO geo_points VALUES
(NOW, 'road', 'LINESTRING(116.397 39.908, 116.407 40.080)');
-- 插入多边形
INSERT INTO geo_points VALUES
(NOW, 'area', 'POLYGON((116.3 39.9, 116.4 39.9, 116.4 40.0, 116.3 40.0, 116.3 39.9))');
2. 查询数据
sql
-- 查询所有数据(自动以 WKT 格式返回,无需 ST_AsText)
SELECT * FROM geo_points;
输出示例:
ts | name | geom |
===================================================================================
1765100256637 | tiananmen | POINT (116.397000 39.908000) |
1765100256640 | airport | POINT (116.407000 40.080000) |
1765100256650 | road | LINESTRING (116.397 39.908, 116.407 40.08) |
1765100256660 | area | POLYGON ((116.3 39.9, 116.4 39.9, ...)) |
地理关系判断函数
ST_Intersects - 相交判断
判断两个几何对象是否相交(有任何共享点)。
sql
-- 在 SELECT 中使用(注意:必须用 ST_GeomFromText 转换文本参数)
SELECT
name,
geom,
ST_Intersects(
geom,
ST_GeomFromText('POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.1, 116.3 40.1, 116.3 39.8))')
) AS is_intersect
FROM geo_points;
-- 判断两个几何对象是否相交
SELECT ST_Intersects(
ST_GeomFromText('LINESTRING(0 0, 1 1)'),
ST_GeomFromText('LINESTRING(0 1, 1 0)')
) AS is_intersect;
-- 返回: true
⚠️ 注意: 不能在 WHERE 子句中使用,以下语法不支持:
sql
-- ❌ 错误用法:WHERE 中不支持函数
SELECT name FROM geo_points
WHERE ST_Intersects(geom, ST_GeomFromText('POLYGON(...)'));
ST_Contains - 包含判断
判断几何对象 A 是否完全包含几何对象 B。
sql
-- 在 SELECT 中判断区域是否包含点
SELECT
name,
geom,
ST_Contains(
ST_GeomFromText('POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.1, 116.3 40.1, 116.3 39.8))'),
geom
) AS is_contained
FROM geo_points;
-- 判断多边形是否包含点
SELECT ST_Contains(
ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
ST_GeomFromText('POINT(5 5)')
) AS is_contained;
-- 返回: true
ST_ContainsProperly - 严格包含判断
判断 B 的每个点是否都位于 A 的内部(不包括边界)。
sql
-- 边界上的点不算严格包含
SELECT ST_ContainsProperly(
ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
ST_GeomFromText('POINT(0 0)')
) AS is_contained_properly;
-- 返回: false(因为点在边界上)
SELECT ST_ContainsProperly(
ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
ST_GeomFromText('POINT(5 5)')
) AS is_contained_properly;
-- 返回: true(因为点在内部)
ST_Covers - 覆盖判断
判断 B 中的每个点是否都位于 A 内部或边界上。
sql
-- 与 Contains 类似,但包括边界情况
SELECT ST_Covers(
ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
ST_GeomFromText('POINT(0 0)')
) AS is_covered;
-- 返回: true(边界点也算覆盖)
ST_Equals - 空间相等判断
判断两个几何对象是否在空间上相等。
sql
-- 点的顺序不同,但表示相同的多边形
SELECT ST_Equals(
ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
) AS is_equal;
-- 返回: true
-- 相同的点
SELECT ST_Equals(
ST_GeomFromText('POINT(1 1)'),
ST_GeomFromText('POINT(1 1)')
) AS is_equal;
-- 返回: true
ST_Touches - 接触判断
判断两个几何对象是否相接触(相交但内部不相交)。
sql
-- 两个多边形共享边界
SELECT ST_Touches(
ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
ST_GeomFromText('POLYGON((1 0, 2 0, 2 1, 1 1, 1 0))')
) AS is_touching;
-- 返回: true
-- 点/点不会触碰(点没有边界)
SELECT ST_Touches(
ST_GeomFromText('POINT(0 0)'),
ST_GeomFromText('POINT(0 0)')
) AS is_touching;
-- 返回: false
实际应用场景
场景 1:查找附近的地点
sql
-- 创建兴趣点表
CREATE TABLE poi (
ts TIMESTAMP,
name VARCHAR(100),
category VARCHAR(50),
location GEOMETRY(256)
);
-- 插入数据
INSERT INTO poi VALUES
(NOW, '北京天安门', '景点', 'POINT(116.397 39.908)'),
(NOW, '故宫博物院', '景点', 'POINT(116.397 39.918)'),
(NOW, '王府井', '商圈', 'POINT(116.410 39.915)'),
(NOW, '首都机场', '交通', 'POINT(116.407 40.080)');
-- 查找矩形区域内的所有景点(在 SELECT 中返回判断结果)
SELECT
name,
location,
ST_Intersects(
location,
ST_GeomFromText('POLYGON((116.38 39.90, 116.42 39.90, 116.42 39.93, 116.38 39.93, 116.38 39.90))')
) AS in_area
FROM poi
WHERE category = '景点';
-- 应用层需要根据 in_area 字段过滤结果
场景 2:区域覆盖分析
sql
-- 创建服务区域表
CREATE TABLE service_area (
ts TIMESTAMP,
store_name VARCHAR(100),
coverage GEOMETRY(512)
);
-- 插入服务范围(圆形区域可用多边形近似)
INSERT INTO service_area VALUES
(NOW, '门店A', 'POLYGON((116.3 39.9, 116.35 39.88, 116.4 39.9, 116.35 39.92, 116.3 39.9))');
-- 查找能覆盖指定地点的门店(在 SELECT 中计算)
SELECT
store_name,
coverage,
ST_Contains(
coverage,
ST_GeomFromText('POINT(116.33 39.90)')
) AS can_serve
FROM service_area;
场景 3:批量地理计算
sql
-- 创建设备位置表
CREATE TABLE device_location (
ts TIMESTAMP,
device_id VARCHAR(50),
position GEOMETRY(128)
);
-- 创建围栏区域表
CREATE TABLE geofence (
ts TIMESTAMP,
fence_id INT,
fence_name VARCHAR(100),
boundary GEOMETRY(512)
);
-- 计算设备与围栏的关系(应用层过滤)
SELECT
d.device_id,
d.position,
g.fence_name,
ST_Contains(g.boundary, d.position) AS inside_fence
FROM device_location d, geofence g
WHERE d.ts >= NOW - 1h;
转换函数说明
ST_GeomFromText - WKT 转 GEOMETRY(必需)
功能:将 WKT 文本转换为 GEOMETRY 类型
用途:
- 向地理函数传递常量 WKT 参数时必须使用
- INSERT 语句中不需要使用(自动转换)
sql
-- ✅ 正确:函数参数必须使用 ST_GeomFromText
SELECT ST_Contains(
ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
geom
) FROM geo_points;
-- ❌ 错误:直接传递文本字符串
SELECT ST_Contains(
'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))',
geom
) FROM geo_points;
-- ✅ 正确:INSERT 时可以直接使用文本
INSERT INTO geo_points VALUES (NOW, 'test', 'POINT(1 1)');
ST_AsText - GEOMETRY 转 WKT(可选)
功能:将 GEOMETRY 类型转换为 WKT 文本
用途:
- SELECT 查询结果已自动转换为 WKT 格式,通常不需要显式调用
- 某些特殊场景需要确保文本格式输出时使用
sql
-- 通常不需要(自动转换)
SELECT geom FROM geo_points;
-- 显式转换(结果相同)
SELECT ST_AsText(geom) FROM geo_points;
支持的几何类型
TDengine 支持以下 WKT 几何类型:
- POINT - 点:
'POINT(x y)' - LINESTRING - 线串:
'LINESTRING(x1 y1, x2 y2, ...)' - POLYGON - 多边形:
'POLYGON((x1 y1, x2 y2, ..., x1 y1))' - MULTIPOINT - 多点:
'MULTIPOINT((x1 y1), (x2 y2))' - MULTILINESTRING - 多线串
- MULTIPOLYGON - 多多边形
- GEOMETRYCOLLECTION - 几何集合
使用建议与限制
当前限制
-
WHERE 子句限制:
- ❌ 不支持在 WHERE 中使用地理函数
- ✅ 只能在 SELECT 中计算,然后在应用层过滤结果
-
函数参数传递限制:
- ❌ 不能直接传递文本字符串给函数
- ✅ 必须使用
ST_GeomFromText()转换文本参数
变通方案
由于不支持 WHERE 过滤,建议的使用子查询模式:
sql
SELECT * FROM
(
SELECT
*,
ST_Contains(boundary, position) AS match_result
FROM your_table
)
WHERE match_result = 'true'
性能优化建议
-
使用常量优化:当查询中一个参数为常量时,TDengine 会自动创建 PreparedGeometry 以提升性能
sql-- 好的实践:常量作为第一个参数 SELECT ST_Contains( ST_GeomFromText('POLYGON(...)'), -- 常量 location -- 变量 ) FROM poi; -
减少计算量:由于无法在 WHERE 中过滤,建议先用其他条件缩小数据集
sql-- 先用时间范围过滤,再计算地理关系 SELECT *, ST_Intersects(geom, ST_GeomFromText('POLYGON(...)')) AS in_area FROM geo_points WHERE ts >= NOW - 1d; -- 先过滤时间 -
合理设置 GEOMETRY 列长度:
sql-- 简单点位:GEOMETRY(128) -- 复杂多边形:GEOMETRY(512) 或更大
注意事项
- ✅ 地理信息函数需要 TDengine 编译时启用 GEOS 支持
- ✅ GEOMETRY 类型存储的是二进制格式,但输入输出自动转换为 WKT
- ✅ 空间关系判断返回 BOOL 类型(true/false),NULL 输入返回 NULL
- ✅ 坐标系统需要应用层统一管理(如统一使用 WGS84 经纬度)
- ⚠️ 不支持在 WHERE 子句中使用地理函数
- ⚠️ 向函数传递 WKT 参数时必须使用 ST_GeomFromText 转换
以上示例展示了 TDengine 地理信息函数的正确用法。关键要点:
- INSERT 时:直接使用 WKT 字符串
- 函数参数 :必须用
ST_GeomFromText()转换 - WHERE 过滤:不支持,需在应用层处理
- SELECT 输出:自动转换为 WKT 格式