"""Builds a self-contained, transparent 3D globe (Globe.gl / Three.js) as an
iframe srcdoc, with flight points and animated estimated-path arcs."""
from __future__ import annotations
import base64
import json
_TEMPLATE = r"""
>> FLIGHTDECK // LIVE TELEMETRY
TARGETS: 0
▣ flight
▣ destination
╌╌ path to destination
"""
def build_globe_html(flights, stamp="", focus=None) -> str:
"""flights: list of dicts from app.normalize_flight(). Returns an iframe."""
points, arcs = [], []
dest_seen = {} # dedup destination cubes by rounded coordinate
for f in flights:
lat, lon = f.get("lat"), f.get("lon")
if lat is None or lon is None:
continue
alt = f.get("alt")
# Plain-text tooltip (htmlElement uses the title attribute).
label = (
f"{f.get('callsign') or f.get('flight') or '??'} "
f"{f.get('orig') or '?'} -> {f.get('dest') or '?'} | "
f"alt {alt or '?'} ft · {f.get('gspeed') or '?'} kt · "
f"ETA {f.get('eta_human') or '—'}"
)
points.append({
"lat": lat, "lng": lon, "kind": "flight",
"alt": 0.015, # sit just above the surface (a marker, not a pole)
"label": label,
})
ep = f.get("est_path")
if ep:
arcs.append({
"sLat": lat, "sLng": lon,
"eLat": ep[0], "eLng": ep[1],
"color": ["#39FF14", "#aaffcc"],
"label": f"{f.get('callsign') or '??'} → {f.get('dest') or '?'} · ETA {f.get('eta_human') or '—'}",
})
# Grey cube marking the destination airport (deduplicated).
key = (round(ep[0], 3), round(ep[1], 3))
if key not in dest_seen:
dest_seen[key] = True
points.append({
"lat": ep[0], "lng": ep[1], "kind": "dest",
"alt": 0.015,
"label": f"DEST {f.get('dest') or '?'}",
})
data = {"points": points, "arcs": arcs, "stamp": stamp, "focus": focus}
html = _TEMPLATE.replace("/*__DATA__*/", json.dumps(data))
b64 = base64.b64encode(html.encode("utf-8")).decode("ascii")
return (
f''
)