文档

轨迹模型

更新时间:

本文介绍了轨迹模型的用途、基本构成和快速入门等内容。

模型用途

简介

轨迹模型是符合一定条件的移动对象集合,通常应用于交通、物流、出行、汽车等领域。

Ganos Trajectory是对象关系型数据库PostgreSQL兼容版本(PolarDB PostgreSQL版(兼容Oracle))的一个时空引擎扩展,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

则这三个点组合起来,就构成了一条时空轨迹,其形状大致如下图所示:

image

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参考