本文介紹GanosBase軌跡引擎的採樣點處理能力,包含軌跡過濾、軌跡切分、軌跡重採樣和軌跡簡化等,協助您快速掌握移動對象的預先處理方法,提升業務開發的便捷性。
GanosBase軌跡模型
軌跡(Trajectory)是時Null 物件中的一個重要模型,旨在支援對行人、汽車、船隻、飛機等移動對象的處理與分析。
軌跡資料在應用中可以從兩種視角進行分析。一種視角是將軌跡視為離散的點集,從而對其進行操作。另一種視角是將軌跡視為一條連續的線,表示為隨時間變化的空間折線。這兩種表示方法各有適用的範圍。連續的軌跡線對採樣頻率不敏感,可作為折線進行各類空間運算。相較之下,離散的軌跡點對採樣方式和採樣頻率的敏感性更高,但在演算法上更加友好和簡單。常見的相似性計算、軌跡切分等函數通常基於對點的操作。
例如,某共用單車在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的時空資料庫GanosBase原生構建軌跡引擎,新增Trajectory類型、索引及相關時空運算元,以解決移動對象的儲存、檢索與分析問題。
軌跡採樣點常用處理操作
背景
在實際業務應用中,軌跡通常由TMS、AIS等系統根據GPS訊號進行採樣併入庫。儘管GPS採集的頻率相對均勻,但由於受到車輛自身行駛狀態的限制,往往會出現以下影響軌跡使用的問題:
GPS訊號引起的軌跡點異常,例如:由於GPS訊號不良導致的錨點漂移所產生的噪點等。
運輸行為導致的無效軌跡點偏多,例如:船舶在進港前等待泊位時在港口周邊環繞、車輛在服務區停留休息等情況。
部分軌跡點的丟失導致整條軌跡的採樣不均勻,例如:由於裝置損壞或人為因素等原因造成的軌跡採樣不均勻,進而導致後續的軌跡分析(如計算相似性等)出現較大誤差。
軌跡資料量大導致前端繪圖效率低下,例如:數億個軌跡點的前端渲染可能導致程式無法載入,從而出現卡頓現象。
功能解析
為解決上述問題,GanosBase在5.2及後續版本中提供了一整套軌跡採樣點處理函數,以便於處理軌跡資訊,從而滿足業務分析與渲染展示等需求。
ST_removeDriftPoints(軌跡過濾)
ST_removeDriftPoints函數主要用於剔除軌跡線上明顯出現錯誤運動趨勢的點,例如,如果一個點的偏離程度顯著大於其他點,這很可能是由於採樣誤差所致。在許多情況下,在進行下一步深度分析之前,需要對這些漂移點進行刪除。同時,除了距離漂移之外,在採樣比較密集但精度不足的情境中,物體在短時間內可能會在一個範圍內不斷跳躍,導致出現極快的速度。這種速度異常點通常也需要被剔除。詳情請參考ST_removeDriftPoints。

ST_Split(軌跡切分)
軌跡採樣點處理的一個關鍵難題在於業務需求的多樣性。不同業務需要根據自身的特定需求對軌跡點進行修正,因此,提供便捷的軌跡切分能力,使使用者能夠自主處理每段軌跡,已成為一項基本需求。軌跡切分一般分為三種情境:
切分點(cut_point):選取若干軌跡的採樣點,將軌跡在這些採樣點處進行斷開(如下圖所示,在點B處進行切分,切分後形成的兩條子軌跡均包含點B)。常見應用情境:當軌跡過長時,將其切分為幾段較短的軌跡。
切分邊(cut_edge):類似於切分軌跡點的操作,但可選取的點不再局限於原先的採樣點,而是可以選擇位於軌跡邊界的點(例如,通過插值得到的點C,從點C上進行切分,兩側均包含點C)。常見應用情境:將軌跡切分成固定形狀,例如按照時間網格或空間網格進行切分。
去除邊(drop_edge):選取一條邊並將其刪除後,該邊的兩個端點將分別歸屬於兩個子軌跡。常見應用情境包括提取軌跡中具有重要意義的部分。例如,可以刪除共用單車停留時的軌跡段,而保留其運行時的軌跡。

GanosBase提供了ST_Split函數,用幾何對象將一條軌跡切分為多條(子)軌跡。詳情請參考ST_Split。
ST_Resample(軌跡重採樣)
在通常情況下,由於去除噪點或其他人工預先處理的影響,軌跡點往往會變得分散不均,這會對局部軌跡段的表現及可分析性產生負面影響。因此,我們需要對軌跡進行重採樣,以使被簡化的軌跡重新獲得均勻的採樣分布。GanosBase設計了兩種重採樣策略:
add_point:升採樣,將點的密度增大,以對軌跡點進行密度統計,相似性匹配等功能。
drop_point:降採樣,將點的密度降低,擷取軌跡在更長時間上的趨勢,可以作為另一種軌跡簡化功能。
GanosBase提供了ST_Resample函數,對一條軌跡重採樣。詳情請參考ST_Resample。
軌跡簡化
軌跡簡化,即軌跡的有損壓縮。在保留軌跡大致移動方向的基礎上,減少軌跡採樣點的數量。軌跡壓縮能夠有效降低資料存放區需求,並提升渲染能力。

如上圖所示,二維軌跡的簡化通常採用道格拉斯-普克演算法。首先,需要指定一個容差,以表徵簡化版本與原始軌跡之間的最大差異。隨後,在軌跡首尾相連的基礎上,依次判斷各點是否滿足容差要求。該過程通過不斷重複“選擇差距最大的點-擷取新的簡化軌跡-判斷差異是否小於容差”這一系列步驟進行,直至原始軌跡上所有點到簡化軌跡的距離均小於所設定的容差。然而,需要注意的是,道格拉斯-普克演算法僅適用於二維軌跡,並要求所有軌跡採樣點均位於同一平面上。但是在時空軌跡的問題上,可能會出現軌跡在某一點停留較長時間的情況。從三維角度進行觀察,此時軌跡將形成一條與時間軸平行的長直線。而從二維角度來看,這條直線則會變為一個點。根據二維的道格拉斯-普克演算法,該直線的起點和終點將被捨棄其中一個,從而破壞軌跡的三維空間結構。為此,針對具有時間戳記的軌跡,GanosBase採用同步時間歐式距離(Synchronized Euclidean Distance, SED)來替代上述的垂直歐式距離,以確保軌跡的時空壓縮更加合理和有效。
GanosBase提供以下函數進行軌跡簡化:
函數名稱 | 描述 | 相關文檔 |
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;