-- migrate:up -- -- Continuous day track for a vehicle: one GeoJSON LineString of every fix in -- the EAT day, in time order. serve.fn_vehicle_trips returns each trip as its -- own ST_MakeLine, so on the map a day with reporting-gap trip splits reads as -- several disconnected segments. The frontend draws this track as a faint base -- line under the coloured per-trip segments, so the route looks like one -- continuous drive while individual trips stay highlighted. -- -- EAT day boundary matches serve.fn_vehicle_trips (UTC+3). Light ST_Simplify -- (~3 m) trims redundant points to keep the payload small without visibly -- changing the shape. Returns NULL when there are fewer than 2 fixes. CREATE OR REPLACE FUNCTION serve.fn_vehicle_day_track( p_vehicle_id bigint, p_date_eat date ) RETURNS jsonb LANGUAGE sql STABLE AS $$ WITH bounds AS ( SELECT (p_date_eat::timestamp - interval '3 hours') AT TIME ZONE 'UTC' AS day_start ), -- Use one device's fixes (tracker-first, same canonical pick as -- fn_vehicle_trips) so a camera-paired vehicle's track doesn't zig-zag -- between the tracker and camera positions. primary_imei AS ( SELECT d.imei FROM domain.devices d WHERE d.vehicle_id = p_vehicle_id ORDER BY CASE d.device_type WHEN 'tracker' THEN 0 ELSE 1 END, d.imei LIMIT 1 ), pts AS ( SELECT ph.geom, ph.occurred_at FROM state.position_history ph, bounds b, primary_imei pi WHERE ph.vehicle_id = p_vehicle_id AND ph.imei = pi.imei AND ph.occurred_at >= b.day_start AND ph.occurred_at < b.day_start + interval '24 hours' ) SELECT CASE WHEN count(*) >= 2 THEN ST_AsGeoJSON( ST_Simplify(ST_MakeLine(geom ORDER BY occurred_at), 0.00003) )::jsonb ELSE NULL END FROM pts; $$; -- migrate:down DROP FUNCTION IF EXISTS serve.fn_vehicle_day_track(bigint, date);