本文介绍Ganos轨迹引擎的采样点处理能力,包含轨迹过滤、轨迹切分、轨迹重采样和轨迹简化等,帮助您快速掌握移动对象的预处理方法,提升业务开发的便捷性。
Ganos轨迹模型
轨迹(Trajectory)是时空对象中的一个重要模型,旨在支持对行人、汽车、船只、飞机等移动对象的处理与分析。
轨迹数据在应用中可以从两种视角进行分析。一种视角是将轨迹视为离散的点集,从而对其进行操作。另一种视角是将轨迹视为一条连续的线,表示为随时间变化的空间折线。这两种表示方法各有适用的范围。连续的轨迹线对采样频率不敏感,可作为折线进行各类空间运算。相较之下,离散的轨迹点对采样方式和采样频率的敏感性更高,但在算法上更加友好和简单。常见的相似度计算、轨迹切分等函数通常基于对点的操作。
例如,某共享单车在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 |
则这三个点组合起来,就构成了一条时空轨迹,其形状大致如下图所示:
依托云原生数据库PolarDB的时空数据库Ganos原生构建轨迹引擎,新增Trajectory类型、索引及相关时空算子,以解决移动对象的存储、检索与分析问题。
轨迹采样点常用处理操作
背景
在实际业务应用中,轨迹通常由TMS、AIS等系统根据GPS信号进行采样并入库。尽管GPS采集的频率相对均匀,但由于受到车辆自身行驶状态的限制,往往会出现以下影响轨迹使用的问题:
GPS信号引起的轨迹点异常,例如:由于GPS信号不良导致的定位点漂移所产生的噪点等。
运输行为导致的无效轨迹点偏多,例如:船舶在进港前等待泊位时在港口周边环绕、车辆在服务区停留休息等情况。
部分轨迹点的丢失导致整条轨迹的采样不均匀,例如:由于设备损坏或人为因素等原因造成的轨迹采样不均匀,进而导致后续的轨迹分析(如计算相似度等)出现较大误差。
轨迹数据量大导致前端绘图效率低下,例如:数亿个轨迹点的前端渲染可能导致程序无法加载,从而出现卡顿现象。
功能解析
为解决上述问题,Ganos在5.2及后续版本中提供了一整套轨迹采样点处理函数,以便于处理轨迹信息,从而满足业务分析与渲染展示等需求。
ST_removeDriftPoints(轨迹过滤)
ST_removeDriftPoints函数主要用于剔除轨迹线上明显出现错误运动趋势的点,例如,如果一个点的偏离程度显著大于其他点,这很可能是由于采样误差所致。在许多情况下,在进行下一步深度分析之前,需要对这些漂移点进行删除。同时,除了距离漂移之外,在采样比较密集但精度不足的场景中,物体在短时间内可能会在一个范围内不断跳跃,导致出现极快的速度。这种速度异常点通常也需要被剔除。详情请参考ST_removeDriftPoints。
ST_Split(轨迹切分)
轨迹采样点处理的一个关键难题在于业务需求的多样性。不同业务需要根据自身的特定需求对轨迹点进行修正,因此,提供便捷的轨迹切分能力,使用户能够自主处理每段轨迹,已成为一项基本需求。轨迹切分一般分为三种场景:
切分点(cut_point):选取若干轨迹的采样点,将轨迹在这些采样点处进行断开(如下图所示,在点B处进行切分,切分后形成的两条子轨迹均包含点B)。常见应用场景:当轨迹过长时,将其切分为几段较短的轨迹。
切分边(cut_edge):类似于切分轨迹点的操作,但可选取的点不再局限于原先的采样点,而是可以选择位于轨迹边界的点(例如,通过插值得到的点C,从点C上进行切分,两侧均包含点C)。常见应用场景:将轨迹切分成固定形状,例如按照时间网格或空间网格进行切分。
去除边(drop_edge):选取一条边并将其删除后,该边的两个端点将分别归属于两个子轨迹。常见应用场景包括提取轨迹中具有重要意义的部分。例如,可以删除共享单车停留时的轨迹段,而保留其运行时的轨迹。
Ganos提供了ST_Split函数,用几何对象将一条轨迹切分为多条(子)轨迹。详情请参考ST_Split。
ST_Resample(轨迹重采样)
在通常情况下,由于去除噪点或其他人工预处理的影响,轨迹点往往会变得分散不均,这会对局部轨迹段的表现及可分析性产生负面影响。因此,我们需要对轨迹进行重采样,以使被简化的轨迹重新获得均匀的采样分布。Ganos设计了两种重采样策略:
add_point:升采样,将点的密度增大,以对轨迹点进行密度统计,相似度匹配等功能。
drop_point:降采样,将点的密度降低,获取轨迹在更长时间上的趋势,可以作为另一种轨迹简化功能。
Ganos提供了ST_Resample函数,对一条轨迹重采样。详情请参考ST_Resample。
轨迹简化
轨迹简化,即轨迹的有损压缩。在保留轨迹大致移动方向的基础上,减少轨迹采样点的数量。轨迹压缩能够有效降低数据存储需求,并提升渲染能力。
如上图所示,二维轨迹的简化通常采用道格拉斯-普克算法。首先,需要指定一个容差,以表征简化版本与原始轨迹之间的最大差异。随后,在轨迹首尾相连的基础上,依次判断各点是否满足容差要求。该过程通过不断重复“选择差距最大的点-获取新的简化轨迹-判断差异是否小于容差”这一系列步骤进行,直至原始轨迹上所有点到简化轨迹的距离均小于所设定的容差。然而,需要注意的是,道格拉斯-普克算法仅适用于二维轨迹,并要求所有轨迹采样点均位于同一平面上。但是在时空轨迹的问题上,可能会出现轨迹在某一点停留较长时间的情况。从三维角度进行观察,此时轨迹将形成一条与时间轴平行的长直线。而从二维角度来看,这条直线则会变为一个点。根据二维的道格拉斯-普克算法,该直线的起点和终点将被舍弃其中一个,从而破坏轨迹的三维空间结构。为此,针对具有时间戳的轨迹,Ganos采用同步时间欧式距离(Synchronized Euclidean Distance, SED)来替代上述的垂直欧式距离,以确保轨迹的时空压缩更加合理和有效。
Ganos提供以下函数进行轨迹简化:
函数名称 | 描述 | 相关文档 |
ST_Compress | 将Trajectory对象按道格拉斯-普克算法进行的二维轨迹简化。 | |
ST_CompressSED | 将Trajectory对象按SED距离的时空轨迹(二维+时间)简化。 |
最佳实践
案例一:剔除船舶经纬度信息漂移点
某船舶客户,通过获取AIS系统向数据库的点表中插入船舶的经纬度信息。为此,需要将该表处理为轨迹对象,并剔除漂移点。
SELECT
ST_removeDriftPoints (-- 删除漂移点
ST_SetSRID ( -- 设置SRID
ST_MakeTrajectory ( -- 构造轨迹对象
ARRAY_AGG ( ROW ( traj.arrival_time :: TIMESTAMP, st_x ( traj.pts ) :: DOUBLE PRECISION, traj.lat :: DOUBLE PRECISION, traj.rowid )
), FALSE, '{"rowid"}' :: cstring [] ), 4326 ),
40,
10,
'1 minute' :: INTERVAL
)
FROM
(
SELECT
time
ST_makepoint ( lon, lat ) pts,
lat,
rowid
FROM
point_table
WHERE
time IS NOT NULL
AND lon IS NOT NULL
AND lat IS NOT NULL
ORDER BY
rowid
) traj INTO trajectory_table;
案例二:重采样并统计无人机轨迹点密度
某无人机客户,需要将轨迹按照5分钟进行分段重采样,并统计轨迹的点密度。随后,将经过打标的数据传递给机器学习模型,作为特征输入。
SELECT
ST_Density(
ST_Resample(ST_OnlyST(traj),
'{"add_point.period_lesser":"5 minute"}')
), 100, '30 minute'
) FROM table;
案例三:简化货运车辆轨迹
某货运车辆管理客户,在业务中,需要采集整个车辆的长轨迹,但在分析过程中需要去除规矩中较长的边,以便生成用于后续分析的按某段行程的短轨迹。
With traj AS(
SELECT
'{"trajectory":{"version":1,"type":"STPOINT","leafcount":19,"start_time":"2000-01-01 00:01:19.067179","end_time":"2000-01-01 03:24:25.946085","spatial":"LINESTRING(-100 -100 -100,-88.8925775739675 -86.6512698383691 -92.3767832526937,-79.6904716538265 -80.6515727923252 -84.2357598245144,-75.8435507711644 -73.7572890928326 -80.5007370118983,-70.6238425321256 -67.8213750167439 -74.5733173238113,-61.6014582272619 -61.0636760429479 -67.9874239303172,-56.1098577060426 -54.4264591250879 -64.5007972046733,-46.9800617334743 -49.4026757289345 -61.6160059720278,-41.7122942996211 -46.3224360072054 -56.5283147455193,-35.5646221285375 -38.1688933617746 -49.2775720101781,-31.7230528349367 -33.6970051738123 -44.1693710885011,-23.1585765127093 -26.5895827477798 -40.6539742602035,-16.7020264320696 -21.6133877349397 -37.3055470525287,-12.1044529232507 -14.1236051704424 -28.2295028120279,-3.77185660181567 -7.74744770256802 -24.3842111621052,0.488159407706304 -3.68223926316326 -19.9478872027248,6.33406881305078 4.54123636645575 -15.0410129944794,15.6666049417108 10.5611746329814 -11.2770220567472,14 11 -10)","timeline":["2000-01-01 00:01:19.067179","2000-01-01 00:12:36.116007","2000-01-01 00:23:53.164835","2000-01-01 00:35:10.213663","2000-01-01 00:46:27.262491","2000-01-01 00:57:44.311319","2000-01-01 01:09:01.360147","2000-01-01 01:20:18.408975","2000-01-01 01:31:35.457803","2000-01-01 01:42:52.506631","2000-01-01 01:54:09.555459","2000-01-01 02:05:26.604287","2000-01-01 02:16:43.653115","2000-01-01 02:28:00.701943","2000-01-01 02:39:17.750771","2000-01-01 02:50:34.799599","2000-01-01 03:01:51.848427","2000-01-01 03:13:08.897255","2000-01-01 03:24:25.946085"]}}'::trajectory as a
)
SELECT (ST_split(a, '{"drop_edge.spatial_distance_2d":10}')) FROM traj;