diff --git a/tasks/flightradar24/frontend/main.py b/tasks/flightradar24/frontend/main.py index d421ac9..b4f4d22 100644 --- a/tasks/flightradar24/frontend/main.py +++ b/tasks/flightradar24/frontend/main.py @@ -416,6 +416,7 @@ def tracks(): for r in rows: pts = r["points"] if isinstance(r["points"], list) else [] coords = [[p["lon"], p["lat"]] for p in pts if p.get("lon") is not None] + times = [p["ts"].isoformat() if hasattr(p.get("ts"), "isoformat") else str(p["ts"]) if p.get("ts") else None for p in pts if p.get("lon") is not None] if len(coords) < 2: continue features.append({ @@ -427,6 +428,7 @@ def tracks(): "icao24": r["icao24"], "callsign": r["callsign"] or r["icao24"], "point_count": len(coords), + "times": times, }, }) diff --git a/tasks/flightradar24/frontend/static/index.html b/tasks/flightradar24/frontend/static/index.html index 1fa3b1b..30397ea 100644 --- a/tasks/flightradar24/frontend/static/index.html +++ b/tasks/flightradar24/frontend/static/index.html @@ -125,7 +125,7 @@ function closePopup() { async function refreshTracks() { try { - const res = await fetch('/api/tracks?minutes=' + getMinutes()); + const res = await fetch('/api/tracks?minutes=' + getMinutes() + '&limit=200'); if (!res.ok) return; const geojson = await res.json(); let features = geojson.features || []; @@ -138,15 +138,32 @@ async function refreshTracks() { trackLayers = []; for (const f of features) { - const coords = f.geometry.coordinates.map(([lon, lat]) => [lat, lon]); + const pts = f.geometry.coordinates; // [[lon, lat], ...] + const times = (f.properties.times || []); // ISO strings, may be empty + + // Split into segments where time gap > 5 min (300s) + const segments = []; + let seg = []; + for (let i = 0; i < pts.length; i++) { + if (i > 0 && times.length === pts.length) { + const dt = (new Date(times[i]) - new Date(times[i-1])) / 1000; + if (dt > 300) { segments.push(seg); seg = []; } + } + seg.push([pts[i][1], pts[i][0]]); // [lat, lon] for Leaflet + } + if (seg.length) segments.push(seg); + const callsign = f.properties.callsign || f.properties.icao24; - const line = L.polyline(coords, { - color: '#58a6ff', - weight: 3, - opacity: 0.7, - }).addTo(map); - line.bindPopup(callsign); - trackLayers.push(line); + for (const segment of segments) { + if (segment.length < 2) continue; + const line = L.polyline(segment, { + color: '#58a6ff', + weight: 3, + opacity: 0.7, + }).addTo(map); + line.bindPopup(callsign); + trackLayers.push(line); + } } } catch (_) { /* silent — tracks are non-critical */ } } diff --git a/tasks/flightradar24/ingest/preprocess/main.py b/tasks/flightradar24/ingest/preprocess/main.py index e84f517..768ff11 100644 --- a/tasks/flightradar24/ingest/preprocess/main.py +++ b/tasks/flightradar24/ingest/preprocess/main.py @@ -286,6 +286,11 @@ def process_batch(conn, packets: list) -> int: aircraft_id = upsert_aircraft(conn, icao24, callsign, now) flight_id = get_or_create_flight(conn, aircraft_id, callsign, observed_at) + # sanity-check: skip obviously invalid coordinates + if lat is not None and lon is not None and not (-90 <= lat <= 90 and -180 <= lon <= 180): + log.warning("Invalid coords icao24=%s lat=%s lon=%s — skipping", icao24, lat, lon) + lat, lon = None, None + if lat is not None and lon is not None: track_id = get_or_create_track(conn, flight_id) append_track_point(