Ganos Trajectory is a spatio-temporal engine extension for PolarDB for PostgreSQL. It introduces the trajectory data type, which stores a moving object's complete path as a single compressed object rather than one row per sampling point. Because the trajectory object encodes the full timeline, GanosBase can extract sub-trajectories, measure path similarity, and apply spatial or temporal filters — all without complex aggregation across millions of rows.
Typical use cases include vehicle tracking, logistics routing, shared-mobility archiving, and user behavior analysis for recommendation systems.
Key concepts
Trajectory structure
A trajectory records the spatial positions of a moving object at successive time points. Each sampling point contributes a timestamp, a spatial coordinate, and optional attributes such as speed or direction.
For example, a moving object that reports its position three times generates the following data:
| 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 Trajectory aggregates all sampling points of one object into a single trajectory value. The key advantage over a row-per-point table is that GanosBase can perform object-level operations — intersection detection, sub-trajectory extraction, and similarity scoring — that would otherwise require complex aggregation queries.

Core features
GanosBase Trajectory supports the following features for trajectory data storage and analysis:
Trajectory construction: Build trajectories from data sources such as geometries, timestamps, arrays, and data tables.
Trajectory editing: Edit the time, geometries, attributes, and events of a trajectory. Supported operations include simplifying, splitting, and smoothing a trajectory, extracting sub-trajectories, and editing the spatio-temporal features of a trajectory.
Trajectory analysis: Identify spatial relationships and spatio-temporal relationships, analyze similarity, and extract features of trajectories.
Trajectory indexing: Create a spatio-temporal index to accelerate queries and analyses.
For more information, see Trajectory SQL reference.
Four components
A trajectory value has four components:
Time: the ordered timestamps of all sampling points, stored as
start_time,end_time, and atimelinearray.Space: the spatial shape of the path, stored as a
LINESTRINGin WKT (Well-Known Text) format with an associated SRID.Attributes: per-point attribute arrays (e.g., velocity, accuracy, bearing, acceleration), each with a defined type, byte length, and nullable flag.
Events: time-stamped events that are not tied to a single sampling point, stored as
{type: timestamp}pairs wheretypeis a user-defined integer.
Event attributes
Events record occurrences at specific moments that are independent of sampling frequency. They are stored as 2-tuples in {type: timestamp} format, where type is a user-defined integer and timestamp marks when the event occurred. Use events to represent state changes such as engine start, stop, or alarm triggers.
Storage methods
Three methods are available for storing trajectory data:
| Method | Storage format | Update performance | Query performance | Storage size | Attribute support |
|---|---|---|---|---|---|
| Row-per-point table | One row per sampling point | High | Low | High | Yes |
| Geometry (LINESTRING M) | 2D/3D geometry; M dimension stores timestamps | Moderate | High (spatial) | Low | No |
| Trajectory type | Aggregated trajectory object | Moderate | High (spatio-temporal) | Low | Yes |
The trajectory type (Method 3) extends the geometry approach with attribute and event support, and adds spatio-temporal queries. Use it when you need both spatio-temporal queries and per-point metadata. Use Method 1 when write throughput is the priority and you do not need trajectory-level analysis.
When querying, convert between formats as needed. The SQL section below shows how to convert a row table to a trajectory and back.
Spatial reference system
Ganos uses an integer SRID (Spatial Reference ID) to associate a trajectory with a spatial reference system (SRS). The SRID is embedded in the trajectory value and follows the same conventions as PostGIS geometries. For supported SRIDs, see Spatial reference.
Index
Ganos supports the GiST (Generalized Search Tree) index for trajectory columns. GiST is a balanced tree index that accelerates spatial, temporal, and spatio-temporal filter queries. The following query patterns benefit from a trajgist index:
Spatial filter:
WHERE st_3dintersects(traj, <geometry>)Temporal filter:
WHERE st_TIntersects(traj, <start_timestamp>, <end_timestamp>)Spatio-temporal filter:
WHERE st_3dintersects(traj, <geometry>, <start_timestamp>, <end_timestamp>)
Create a trajectory GiST index with the trajgist access method:
CREATE INDEX tr_index ON traj_table USING trajgist (traj);Implementation standard
Ganos trajectory supports and extends the interfaces defined by the OGC Moving Features standard. Geometric attributes are based on the geometry model. Use ST_trajectorySpatial to extract the spatial component for further processing.
The trajectory_columns view
The trajectory_columns view lists all trajectory-type columns in the current database, read from the system catalog:
| Column | Type | Description |
|---|---|---|
t_table_catalog | varchar(256) | Always postgres |
t_table_schema | varchar(256) | Schema of the table |
t_table_name | varchar(256) | Table name |
t_trajectory_column | varchar(256) | Name of the trajectory column |
SELECT * FROM trajectory_columns;Quick start
This section walks through the full workflow: create the extension, build a trajectory table, insert data, create an index, run spatial and temporal queries, and measure trajectory similarity.
For more advanced usage, see the following Trajectory best practices articles:
Analysis of Common Processing Methods for Ganos Trajectory Sampling Points
Reachability Analysis of Transport Vehicles Based on the Ganos Trajectory Engine
Step 1: Create the extension
CREATE EXTENSION ganos_trajectory WITH SCHEMA public CASCADE;Create the extension in the public schema to avoid permission issues.Step 2: Create a trajectory table
CREATE TABLE traj_table (id integer, traj trajectory);Step 3: Insert trajectory data
ST_MakeTrajectory accepts three timestamp formats: a tsrange, two individual timestamp values, or a timestamp[] array. All three forms produce equivalent trajectories.
INSERT INTO traj_table VALUES
-- Using a tsrange
(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]}}}')),
-- Using start and end timestamps
(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]}}}')),
-- Using a timestamp array
(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]}}}')),
-- Without attributes
(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));Step 4: Create a trajectory index
The trajgist index accelerates spatial, temporal, and spatio-temporal filter queries.
CREATE INDEX tr_index ON traj_table USING trajgist (traj);Step 5: Run queries
Spatial filter — find trajectories that intersect a polygon:
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))'));Temporal filter — find trajectories active during a time window:
SELECT id, traj FROM traj_table
WHERE st_TIntersects(traj,
'2010-01-01 12:30:44'::timestamp,
'2010-01-01 14:30:44'::timestamp);Spatio-temporal filter — combine spatial and temporal constraints:
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);Time range query — get the start and end time of each trajectory:
SELECT st_startTime(traj), st_endTime(traj) FROM traj_table;Expected output:
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)Step 6: Measure trajectory similarity
ST_euclideanDistance computes the Euclidean distance between two trajectories and returns a scalar score. A lower value indicates greater similarity.
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;Expected output:
st_euclideandistance
----------------------
0.0888997369940162
(1 row)Step 7 (optional): Drop the extension
DROP EXTENSION ganos_trajectory CASCADE;Output format
A trajectory is serialized to JSON for text output. The JSON contains four top-level sections corresponding to the four components: time, space, attributes, and events.
{
"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"}
]
}
}Field reference:
| Field | Description |
|---|---|
version | Format version (fixed value) |
type | Leaf type (fixed value, e.g., STPOINT) |
leafcount | Number of sampling points |
start_time / end_time | Start and end timestamps of the trajectory |
spatial | Spatial path in WKT format with SRID prefix |
timeline | Timestamp of each sampling point, as a string array |
attributes.leafcount | Number of elements in each attribute array |
attributes.<name>.type | Data type of the attribute (integer, float, string, timestamp) |
attributes.<name>.length | Byte length of the data type |
attributes.<name>.nullable | Whether the attribute accepts null values |
attributes.<name>.value | Array of attribute values, one per sampling point |
events | Array of {type: timestamp} key-value pairs |
Convert between storage formats
Use the following patterns to move data between row-per-point tables and the trajectory type.
-- Create a source table of individual sampling points
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 a trajectory table
CREATE TABLE trajectory_table (userid numeric PRIMARY KEY, traj trajectory);
-- Aggregate sampling points into trajectory values
INSERT INTO trajectory_table
SELECT userid,
ST_Sort(ST_MakeTrajectory(pnts.tjraw, true, '{"intensity"}'::cstring[]))
FROM (
SELECT userid,
array_agg(ROW(sample_time, x, y, z, intensity)) AS tjraw
FROM sample_points
GROUP BY userid
) pnts;
-- Convert a trajectory back to individual rows
SELECT f.*
FROM trajectory_table,
ST_AsTable(traj) AS f(t timestamp, x double precision, y double precision,
z double precision, intensity integer);
-- Extract the spatial component as a LINESTRING
SELECT ST_trajspatial(traj) FROM trajectory_table;
-- Build a trajectory from a LINESTRING and a time range
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,
'{}');Use cases
Archiving historical trajectories
Aggregate high-frequency GPS pings from devices such as shared bicycles into trajectory objects. Apply trajectory simplification and downsampling algorithms to reduce data volume, then store the results in the database or in OSS buckets. Ganos provides unified query access regardless of storage location, so downstream applications do not need to distinguish between the two.
Spatio-temporal analysis and similarity search
Query which trajectories passed through a specific geographic area during a given time window, or find trajectories most similar to a reference path. Use these results to identify customer segments, support targeted promotions, or detect anomalous movement patterns.
Feature extraction for machine learning
Cleanse historical trajectories by resampling at uniform intervals and removing drift points. Extract statistical features — total length, duration, stay points, and curvature — then feed them into machine learning pipelines (neural network or other algorithms) to build user profiles for recommendation and personalization systems.
What's next
Trajectory SQL reference — full function reference for construction, editing, analysis, and indexing
Spatial reference — supported SRIDs and coordinate systems
ST_trajectorySpatial — extract the spatial component from a trajectory value