本文介绍了轨迹模型的用途、基本构成和快速入门等内容。
模型用途
简介
轨迹模型是符合一定条件的移动对象集合,通常应用于交通、物流、出行、汽车等领域。
Ganos Trajectory是对象关系型数据库PostgreSQL兼容版本(PolarDB PostgreSQL版)的一个时空引擎扩展,Trajectory是Ganos自主研发的一种数据类型,主要用来存储移动对象的采样点和属性信息,并对其执行分析任务。
功能概述
Trajectory支持轨迹数据存储与分析方面的多种功能:
轨迹构建:通过几何形状(geometry)和时间戳、数组、数据表等数据源构建轨迹。
轨迹编辑:编辑轨迹的时间、几何、属性、事件等对象信息,同时支持轨迹的抽稀、切分、平滑、子轨迹提取、时空特征编辑等能力。
轨迹分析:支持轨迹的空间关系判断、时空关系判断、相似度分析、特征提取等功能。
轨迹索引:即创建轨迹时空索引加速上述查询分析。
更多内容请参见Trajectory SQL参考。
主要业务场景
下面列举了几个典型的轨迹模型应用场景。
历史轨迹的归档存储
将共享单车的一类的高频采样点聚合成为轨迹,利用轨迹简化算法进行简化和降采样后,将简化后的轨迹存储到数据库中或外部OSS存储中,以节省存储空间和存储成本。Ganos支持以统一的方式访问轨迹数据,无需注意其存储在数据表内还是OSS中。
轨迹的时空分析和伴随分析
通过给出一个关注的时空区域或时空轨迹,查找经过这个时空区域的轨迹或是查找和已知轨迹相似的轨迹,从而得到数据库中值得关注的轨迹,用以支持定位一类用户、发放优惠券等业务。
轨迹的特征提取和分析
对用户的历史轨迹,通过重采样和漂移点去除来清洗数据后,提取出长度、持续时间、驻留点、弯道等统计信息,将其作为机器学习的特征信息输入到神经网络或其它算法中,用以生成用户画像来指导业务上对用户的管理和推荐等。
基本构成
概述
现实世界中,一个运动物体会在不同的时刻,记录其空间位置。一个物体的轨迹是一个包含时间和空间信息的点的序列,记录了该物体在不同时刻的空间位置。
例如,某共享单车在2020-04-11 17:42:30时上报了其经纬度坐标(114.35, 39.28),则其在数据库中可以表示为一条记录:
time | x | y |
2020-04-11 17:42:30 | 114.35 | 39.28 |
通常,在轨迹的采样点上,还会记录一些其它信息。例如速度、方向等。这里假设其记录了速度的数据:
time | x | y | speed |
2020-04-11 17:42:30 | 114.35 | 39.28 | 4.3 |
随着时间的推移,将会有一系列轨迹点。这里假设其有三条记录:
time | x | y | speed |
2020-04-11 17:42:30 | 114.35 | 39.28 | 4.3 |
2020-04-11 17:43:30 | 114.36 | 39.28 | 4.8 |
2020-04-11 17:45:00 | 114.35 | 39.29 | 3.5 |
则这三个点组合起来,就构成了一条时空轨迹,其形状大致如下图所示:
Ganos的轨迹模型将一个运动物体的多个轨迹点聚合起来存储,和把每个轨迹点单独存储相比:一方面,我们可以针对多个轨迹点进行压缩,节省存储成本;另一方面,针对聚合后的轨迹,可以执行多种针对整条轨迹而非单个轨迹点的操作,例如:轨迹相交、提取子轨迹,轨迹相似性判断等。
此外,Ganos还支持保存轨迹的事件属性,其记录和单个采样点无关的、在不同时刻发生的事件信息。其格式为{type:timestamp}
的二元组,其中 type 为用户自定义的事件编号(整数类型),timestamp为事件发生的时间。
存储方式
在业务上,对于移动对象的轨迹,常见的三种表示方式:
方式一:存储为表格中的行,每一行分别记录时间、x坐标、y坐标等信息。
方式二:存储为二维或三维的几何类型LINESTRING/LINESTRING M,用M维度存储时间戳。
方式三:存储为轨迹类型。
其中,方式一更新方便,但查询效率较低,存储空间占用较多。方式二降低了更新效率,但增强了空间查询效率,并节约了存储空间,但无法存储属性信息。方式三在方式二的基础上对时间的处理能力进一步增强,同时支持属性和事件。用户可以根据实际需要选择存储时使用的表示方式,并在查询时转化为其它表示方式进行处理和分析。
--构建存储为行的数据,每一行代表一个轨迹点
CREATE TABLE sample_points(userid numeric, sample_time timestamp, x double precision, y double precision, z double precision, intensity int);
INSERT INTO sample_points VALUES
(1,'2020-04-11 17:42:30',114.35, 39.28, 4, 80),
(1,'2020-04-11 17:43:30',114.36, 39.28, 4, 30),
(1,'2020-04-11 17:45:00',114.35, 39.29, 4, 50),
(2,'2020-04-11 17:42:30',114.3, 39, 34, 60),
(2,'2020-04-11 17:43:30',114.3, 39, 38, 58);
--从行类型转化为轨迹类型,每一行代表一个轨迹点
CREATE TABLE trajectory_table(userid numeric PRIMARY KEY, traj trajectory);
INSERT INTO trajectory_table
SELECT userid, ST_Sort(ST_MakeTrajectory(pnts.tjraw, true, '{"intensity"}'::cstring[]))
FROM
(SELECT sample_points.userid, array_agg(ROW(sample_points.sample_time, sample_points.x, sample_points.y, sample_points.z, sample_points.intensity)) as tjraw FROM sample_points GROUP BY userid) pnts;
--从轨迹类型转化为行类型
SELECT f.* from trajectory_table,ST_AsTable(traj) as f(t timestamp, x double precision, y double precision, z double precision, intensity integer);
-- 转化轨迹类型为LINESTRING类型,其中只包含空间信息
SELECT ST_trajspatial(traj) FROM trajectory_table;
-- 使用LINESTRING类型的空间形状和时间范围构建轨迹
SELECT ST_MakeTrajectory('STPOINT'::leaftype, st_geomfromtext('LINESTRING (114 35, 115 36, 116 37)', 4326), '[2010-01-01 14:30, 2010-01-01 15:30)'::tsrange, '{}');
输出格式
Ganos Trajectory中的一条轨迹由时间、空间、属性、事件四个部分构成,在将其转化为文本输出时,也会依照四个部分,格式化为JSON格式进行输出。示例如下:
{
"trajectory": {
"version": 1,
"type": "STPOINT",
"leafcount": 3,
"start_time": "2010-01-01 14:30:00",
"end_time": "2010-01-01 15:30:00",
"spatial": "SRID=4326;LINESTRING(114 35,115 36,116 37)",
"timeline": [
"2010-01-01 14:30:00",
"2010-01-01 15:00:00",
"2010-01-01 15:30:00"
],
"attributes": {
"leafcount": 3,
"velocity": {
"type": "integer",
"length": 2,
"nullable": true,
"value": [120, 130, 140]
},
"accuracy": {
"type": "float",
"length": 4,
"nullable": false,
"value": [120.0, 130.0, 140.0]
},
"bearing": {
"type": "float",
"length": 8,
"nullable": false,
"value": [120.0, 130.0, 140.0]
},
"acceleration": {
"type": "string",
"length": 20,
"nullable": true,
"value": ["120", "130", "140"]
},
"active": {
"type": "timestamp",
"length": 8,
"nullable": false,
"value": [
"2010-01-01 14:30:00",
"2010-01-01 15:00:00",
"2010-01-01 15:30:00"
]
}
},
"events": [
{
"1": "2010-01-01 14:30:00"
},
{
"2": "2010-01-01 15:00:00"
},
{
"3": "2010-01-01 15:30:00"
}
]
}
}
其中:
version、type:固定值。
leafcount:表示轨迹采样点个数。
start_time、end_time:表示轨迹的开始和结束时间。
spatial:表示轨迹的空间形状,以WKT形式显示。
timeline:表示采样点的时间戳,以字符数组的格式显示。
attributes:表示轨迹的属性,其中leafcount是元素个数,其余的键是属性的名称,而属性值中type是数据类型,length是数据类型的长度,nullable为是否接受空值。
events:表示轨迹的事件,其由多个键值对的数组组成。
空间参考系
空间参考系(Spatial Reference System,以下简称为SRS )定义了如何将Trajectory对象关联到地球表面上某个具体位置。
Ganos使用一个整数来表示SRS的定义引用,称为SRID。Trajectory对象通过其自身的SRID值与SRS关联。
更多内容请参见空间参考。
数据列视图
trajectory_columns是从数据库系统目录表中读取全部栅格列的视图,其结构如下:
列名 | 类型 | 说明 |
t_table_catalog | varchar(256) | 一般为固定值postgres。 |
t_table_schema | varchar(256) | 该表所在的schema。 |
t_table_name | varchar(256) | 该表的表名。 |
t_trajectory_column | varchar(256) | 该表中某个Trajectory列的列名。 |
可以通过如下语句查询当前数据库中全部几何数据列:
SELECT * FROM trajectory_columns;
索引
Ganos为轨迹数据提供了GiST索引:
索引名称 | 索引说明 | 索引特点 |
GiST (Generalized Search Tree) | GiST索引是一种平衡搜索树,是最常用、最通用的空间索引方法,提供非常好的查询性能。 | GiST索引允许定义一些规则将任意类型的数据分布在一棵平衡树上,同时也允许定义一些方法访问这些数据。 |
实现标准
Ganos轨迹模型实现了OGC Moving Features标准所定义的接口,同时对其进行了扩展。轨迹中的几何属性依照几何模型进行实现,并可使用ST_trajectorySpatial函数将一条轨迹的几何部分提取出来进行操作。
快速入门
简介
快速入门文档帮助用户快速理解Ganos Trajectory引擎的基本用法,包括扩展创建、创建表、插入数据、创建索引、查询、相似性分析等内容。
更多专业用法可参考Trajectory最佳实践文章:
语法说明
创建扩展。
CREATE extension ganos_trajectory cascade;
说明建议将扩展安装在public模式下,避免权限问题。
CREATE extension ganos_trajectory WITH schema public cascade;
创建轨迹表。
CREATE TABLE traj_table (id integer, traj trajectory);
插入轨迹数据。
INSERT INTO traj_table VALUES (1, ST_MakeTrajectory('STPOINT'::leaftype, st_geomfromtext('LINESTRING (114 35, 115 36, 116 37)', 4326), '[2010-01-01 14:30, 2010-01-01 15:30)'::tsrange, '{"leafcount": 3,"attributes" : {"velocity" : {"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"accuracy":{"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"bearing":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]},"acceleration":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]}}}')), (2, ST_MakeTrajectory('STPOINT'::leaftype, st_geomfromtext('LINESTRING (114 35, 115 36, 116 37)', 4326), '2010-01-01 14:30'::timestamp, '2010-01-01 15:30'::timestamp, '{"leafcount": 3,"attributes" : {"velocity" : {"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"accuracy":{"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"bearing":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]},"acceleration":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]}}}')), (3, ST_MakeTrajectory('STPOINT'::leaftype, st_geomfromtext('LINESTRING (114 35, 115 36, 116 37)', 4326), ARRAY['2010-01-01 14:30'::timestamp, '2010-01-01 15:00'::timestamp, '2010-01-01 15:30'::timestamp], '{"leafcount": 3,"attributes" : {"velocity" : {"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"accuracy":{"type":"integer","length":4,"nullable":false,"value":[120, 130, 140]},"bearing":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]},"acceleration":{"type":"float","length":4,"nullable":false,"value":[120, 130, 140]}}}')), (4, ST_MakeTrajectory('STPOINT'::leaftype, st_geomfromtext('LINESTRING (114 35, 115 36, 116 37)', 4326), '[2010-01-01 14:30, 2010-01-01 15:30)'::tsrange, null));
创建轨迹索引并加速各类查询。
--创建轨迹索引,加速时空过滤效率 CREATE index tr_index ON traj_table USING trajgist (traj); --空间查询时,加速空间过滤 SELECT id, traj FROM traj_table WHERE st_3dintersects(traj, ST_GeomFromText('POLYGON((116.46747851805917 39.92317964155052,116.4986540687358 39.92317964155052,116.4986540687358 39.94452401711516,116.46747851805917 39.94452401711516,116.46747851805917 39.92317964155052))')); --时间查询时,加速时间过滤 SELECT id, traj FROM traj_table WHERE st_TIntersects(traj, '2010-01-01 12:30:44'::timestamp,'2010-01-01 14:30:44'::timestamp); --时空查询时,加速时空过滤 SELECT id, traj FROM traj_table WHERE st_3dintersects(traj, ST_GeomFromText('POLYGON((116.46747851805917 39.92317964155052,116.4986540687358 39.92317964155052,116.4986540687358 39.94452401711516,116.46747851805917 39.94452401711516,116.46747851805917 39.92317964155052))'),'2010-01-01 13:30:44'::timestamp,'2010-01-03 17:30:44'::timestamp);
查询轨迹起、止时间。
SELECT st_startTime(traj), st_endTime(traj) FROM traj_table ; st_starttime | st_endtime ---------------------+--------------------- 2010-01-01 14:30:00 | 2010-01-01 15:30:00 2010-01-01 14:30:00 | 2010-01-01 15:30:00 2010-01-01 14:30:00 | 2010-01-01 15:30:00 2010-01-01 14:30:00 | 2010-01-01 15:30:00 (4 rows)
分析轨迹间的相近性。
With traj AS ( SELECT ST_makeTrajectory('STPOINT', 'LINESTRING(1 1, 5 6, 9 8)'::geometry, '[2010-01-01 11:30, 2010-01-01 15:00)'::tsrange, '{"leafcount":3,"attributes":{"velocity": {"type": "integer", "length": 2,"nullable" : true,"value": [120,130,140]}, "accuracy": {"type": "float", "length": 4, "nullable" : false,"value": [120,130,140]}, "bearing": {"type": "float", "length": 8, "nullable" : false,"value": [120,130,140]}, "acceleration": {"type": "string", "length": 20, "nullable" : true,"value": ["120","130","140"]}, "active": {"type": "timestamp", "nullable" : false,"value": ["Fri Jan 01 11:35:00 2010", "Fri Jan 01 12:35:00 2010", "Fri Jan 01 13:30:00 2010"]}}, "events": [{"2" : "Fri Jan 02 15:00:00 2010"}, {"3" : "Fri Jan 02 15:30:00 2010"}]}') a, ST_makeTrajectory('STPOINT', 'LINESTRING(1 0, 4 2, 9 6)'::geometry, '[2010-01-01 11:30, 2010-01-01 15:00)'::tsrange, '{"leafcount":3,"attributes":{"velocity": {"type": "integer", "length": 2,"nullable" : true,"value": [120,130,140]}, "accuracy": {"type": "float", "length": 4, "nullable" : false,"value": [120,130,140]}, "bearing": {"type": "float", "length": 8, "nullable" : false,"value": [120,130,140]}, "acceleration": {"type": "string", "length": 20, "nullable" : true,"value": ["120","130","140"]}, "active": {"type": "timestamp", "nullable" : false,"value": ["Fri Jan 01 11:35:00 2010", "Fri Jan 01 12:35:00 2010", "Fri Jan 01 13:30:00 2010"]}}, "events": [{"2" : "Fri Jan 02 15:00:00 2010"}, {"3" : "Fri Jan 02 15:30:00 2010"}]}') b) SELECT ST_euclideanDistance(a, b) FROM traj; st_euclideandistance ---------------------- 0.0888997369940162 (1 row)
删除扩展(可选)。
DROP EXTENSION ganos_trajectory CASCADE;
SQL参考
详细SQL手册请参见Trajectory SQL参考。