Splits a trajectory into an array of sub-trajectories. Three overloaded signatures let you split by a spatial geometry object, a named rule, or explicit point indexes.
Syntax
trajectory[] ST_Split(trajectory traj, geometry geom, float8 radius_of_buffer)
trajectory[] ST_Split(trajectory traj, text config)
trajectory[] ST_Split(trajectory traj, int[] indexes)Parameters
| Parameter | Type | Description |
|---|---|---|
traj | trajectory | The trajectory to split. |
geom | geometry | A spatial geometry object used as the split boundary. Only Point and MultiPoint are supported. |
radius_of_buffer | float8 | Radius of the buffer built around the Point geometry, in meters. |
config | text | A JSON string specifying one split rule. See Split rules. |
indexes | int[] | Array of sampling point indexes at which to split. Indexes start from 0. |
How it works
All three signatures return a trajectory[]. Use unnest() to expand the array into individual rows.
Split by geometry — builds a buffer of radius_of_buffer meters around each Point in geom, then cuts the trajectory at every intersection. With a MultiPoint input, the trajectory may be cut at multiple locations.
Split by rule — applies exactly one named rule from the config JSON string. Only one key is allowed per call.
Split by indexes — cuts the trajectory at each specified index. Providing *n* indexes produces *n-1* sub-trajectories. The start and end points of the trajectory do not need to appear in the index array.

The diagram below shows how the three rule families differ: cut_point rules split at a sampling point (Point B); cut_edge rules split at a position along an edge (Point C); drop_edge rules delete an edge entirely (the segment between Point A and Point B).

Split rules
Specify rules as a JSON string with a single key-value pair, for example: '{"cut_point.max_point":4}'.
| Rule | Value type | Behavior |
|---|---|---|
cut_point.max_point | Positive integer | Splits every *n* sampling points (interval-based). |
cut_point.even_divide | Positive integer | Evenly divides the trajectory into *n* parts. If the trajectory has fewer edges than *n*, each edge becomes a sub-trajectory. |
cut_edge.time_interval | Positive time range | Splits at each time boundary, e.g. "50 minute" or "1 hour, 2000-01-01". |
cut_edge.geohash | Positive even integer | Splits at each Geohash grid boundary at the specified precision. The trajectory must use latitude/longitude data. |
drop_edge.temporal_length | Time range | Removes every edge whose temporal length exceeds the threshold. |
drop_edge.spatial_distance_2d | Floating point | Removes every edge whose 2D Euclidean distance exceeds the threshold. |
Only one rule can be specified per call.
Examples
Split by geometry
Create a trajectory table and insert a sample trajectory with 11 sampling points (SRID 4326), then split it using a MultiPoint with a 23,000-meter buffer.
CREATE TABLE tr_split_traj (id integer, traj trajectory);
INSERT INTO tr_split_traj VALUES (
3,
ST_MakeTrajectory(
'STPOINT'::leaftype,
st_geomfromtext(
'LINESTRING(99.027 29.7555,99.313 29.9975,99.852 30.0745,
104.879 35.0795,105.044 35.1235,105.187 35.0685,
109.906 35.0795,110.071 35.1675,110.192 35.0355,
110.544 35.0245,111.017 34.8045)',
4326
),
ARRAY[
'2010-01-01 14:30'::timestamp,
'2010-01-01 15:00','2010-01-01 15:10','2010-01-01 15:20',
'2010-01-01 15:30','2010-01-01 15:40','2010-01-01 15:50',
'2010-01-01 16:00','2010-01-01 16:10','2010-01-01 16:20',
'2010-01-01 16:30'
],
'{"leafcount":11,"attributes":{"velocity":{"type":"integer","length":2,"nullable":true,
"value":[120,130,140,150,160,170,180,190,200,210,220]}}}'
)
);
SELECT id, unnest(st_split(traj, st_geomfromtext('MULTIPOINT(100 30,105 35,110 35)'), 23000)) AS subtraj
FROM tr_split_traj;The query returns 4 sub-trajectories. Each row contains a trajectory JSON with its own leafcount, start_time, end_time, spatial, and timeline fields.
Split by rule
All examples below use a 19-point trajectory defined inline. The cut_point, cut_edge, and drop_edge rules each produce different results.
WITH traj AS (
SELECT '{"trajectory":{"version":1,"type":"STPOINT","leafcount":19,...}}'::trajectory AS a
)cut_point.max_point — split every 4 sampling points
SELECT unnest(ST_split(a, '{"cut_point.max_point":4}')) FROM traj;Returns 5 sub-trajectories: four with 5 points each and a remainder with 3 points.
cut_point.max_point — split every 10 sampling points
SELECT ST_split(a, '{"cut_point.max_point":10}') FROM traj;Returns 2 sub-trajectories with 10 points each.
cut_point.max_point — split every 3 sampling points
SELECT ST_split(a, '{"cut_point.max_point":3}') FROM traj;Returns 7 sub-trajectories.
cut_point.even_divide — divide into 100 equal parts
Because the trajectory has only 18 edges and 100 exceeds that count, each edge becomes its own sub-trajectory.
SELECT ST_split(a, '{"cut_point.even_divide":100}') FROM traj;Returns 18 sub-trajectories, one per edge.
cut_edge.geohash — split at Geohash precision 2
SELECT ST_split(a, '{"cut_edge.geohash":2}') FROM traj;cut_edge.geohash — split at Geohash precision 20
SELECT ST_split(a, '{"cut_edge.geohash":20}') FROM traj;drop_edge.spatial_distance_2d — remove edges longer than 13 units
SELECT ST_split(a, '{"drop_edge.spatial_distance_2d":13}') FROM traj;drop_edge.spatial_distance_2d — remove edges longer than 10 units
SELECT ST_split(a, '{"drop_edge.spatial_distance_2d":10}') FROM traj;drop_edge.spatial_distance_2d — remove edges longer than 1 unit
When the threshold is smaller than all edge distances, every edge is removed and the result is an empty array.
SELECT ST_split(a, '{"drop_edge.spatial_distance_2d":1}') FROM traj;
-- st_split
-- ----------
-- {}
-- (1 row)cut_edge.time_interval — split every 50 minutes
SELECT ST_split(a, '{"cut_edge.time_interval":"50 minute"}') FROM traj;Returns 5 sub-trajectories.
cut_edge.time_interval — split by calendar hour starting at 2000-01-01
SELECT unnest(ST_split(a, '{"cut_edge.time_interval":"1 hour, 2000-01-01"}')) FROM traj;Returns 4 sub-trajectories, each aligned to a one-hour boundary.
Split by index
Split at indexes 1, 3, and 5 (produces 2 sub-trajectories)
SELECT unnest(ST_split(a, '{1,3,5}'::int[])) FROM traj;Returns 2 sub-trajectories: index 1 to 3, and index 3 to 5.
Include start and end explicitly
Pass the first (0) and last (ST_leafcount(a) - 1) indexes alongside interior indexes to include the full trajectory span.
SELECT unnest(ST_split(a, '{0}'::int[] || '{0,1,3,5}'::int[] || ST_leafcount(a) - 1)) FROM traj;Returns 4 sub-trajectories covering the full trajectory from index 0 to the last point.