map and chainage calculation

This commit is contained in:
2026-07-07 22:42:47 +05:30
parent 05ce1f2cdb
commit 308d6bc129
17 changed files with 4178 additions and 48 deletions

View File

@@ -222,3 +222,90 @@ CREATE TABLE IF NOT EXISTS project_folders (
folder TEXT NOT NULL, -- directory part of rel_path ('' = project root)
PRIMARY KEY (project_id, folder)
);
-- ---------------------------------------------------------------------------
-- GPS pipeline (map view + chainage). A background sweep extracts each video's
-- GPS track with exiftool (dashcam Doc{N} samples), one video at a time:
-- gps_status: 'pending' → 'ok' (track stored) | 'none' (no GPS metadata)
-- | 'error' (exiftool/mount failure, see gps_error)
-- The RAW track is immutable once scanned; a kept "Viterbi" correction goes to
-- gps_track_corrected (map/chainage prefer it, raw is never modified).
-- Track format: [[lat, lon, t_s], ...] (t_s = seconds from first sample).
-- ---------------------------------------------------------------------------
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_status TEXT NOT NULL DEFAULT 'pending';
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_track JSONB;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_len_m DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_error TEXT;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_scanned_at TIMESTAMPTZ;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_track_corrected JSONB;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_corrected_by TEXT;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_corrected_at TIMESTAMPTZ;
-- Human-readable summary of what the correction did ("2 spikes · start stitched …").
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_corrected_note TEXT;
-- Data-quality + continuity metadata from the raw scan: absolute epoch of the
-- first/last GPS fix (lets consecutive files stitch across a missing start),
-- raw sample count and the largest inter-fix gap (dropout visibility).
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_start_epoch DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_end_epoch DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_sample_count INTEGER;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS gps_max_gap_s DOUBLE PRECISION;
-- Real media duration read from the container by the exiftool scan. Load-bearing for
-- the stitch guard: an inter-file gap is only "missing start" when the track covers
-- LESS than the file's duration — otherwise the camera simply wasn't recording.
ALTER TABLE videos ADD COLUMN IF NOT EXISTS duration_s DOUBLE PRECISION;
CREATE INDEX IF NOT EXISTS idx_videos_gps ON videos(gps_status) WHERE gps_status = 'pending';
-- ---------------------------------------------------------------------------
-- Road types: admin-defined per-project labels (LHS/RHS/MCW/Servicelane, …)
-- assigned to videos. Injected into the export/claim JSON as `road_type` and
-- re-stamped on push (server-authoritative).
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS project_road_types (
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name TEXT NOT NULL,
PRIMARY KEY (project_id, name)
);
ALTER TABLE videos ADD COLUMN IF NOT EXISTS road_type TEXT NOT NULL DEFAULT '';
-- ---------------------------------------------------------------------------
-- Chainage (linear referencing with piecewise-linear calibration).
-- chainage_points = admin-entered client calibration pairs (lat/lon ↔ chainage).
-- Optional road_type tag scopes a point to one calibration group (LHS vs RHS are
-- separate routes); '' = usable by any group.
-- Per video:
-- sr_chainage_* = ACTUAL measured position along the GPS route (immutable,
-- regenerated only from GPS — never admin-editable)
-- chainage_* = client-calibrated values (error distributed between
-- consecutive control points)
-- route_seq = position of the video along the calibrated route
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS chainage_points (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
chainage_m DOUBLE PRECISION NOT NULL,
note TEXT NOT NULL DEFAULT '',
road_type TEXT NOT NULL DEFAULT '',
created_by TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chainage_points_project ON chainage_points(project_id);
ALTER TABLE videos ADD COLUMN IF NOT EXISTS chainage_start_m DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS chainage_end_m DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS sr_chainage_start_m DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS sr_chainage_end_m DOUBLE PRECISION;
ALTER TABLE videos ADD COLUMN IF NOT EXISTS route_seq INTEGER;
-- The piecewise-linear calibration itself (per project + group), persisted at
-- recompute time as sorted [s_m, chainage_m] anchors. Needed to evaluate chainage at
-- ANY route position later — e.g. frame-exact per-annotation chainage in exports.
CREATE TABLE IF NOT EXISTS chainage_calibrations (
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
road_type TEXT NOT NULL DEFAULT '',
anchors JSONB NOT NULL,
route_len_m DOUBLE PRECISION NOT NULL DEFAULT 0,
computed_by TEXT NOT NULL DEFAULT '',
computed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (project_id, road_type)
);