"""Telemetry endpoint — read flight metadata from an uploaded image. Lets the dashboard auto-fill survey geometry (altitude, FOV, nadir/oblique) from a drone frame's EXIF/XMP instead of asking the user to type it. """ from __future__ import annotations import shutil import tempfile from pathlib import Path from fastapi import APIRouter, File, HTTPException, UploadFile router = APIRouter(prefix="/api/telemetry", tags=["telemetry"]) @router.post("/read") async def read_telemetry(file: UploadFile = File(...)) -> dict: """Parse EXIF/XMP flight telemetry from an uploaded image. Returns what was found (altitude, gimbal angle → nadir/oblique, FOV, GPS), whether it's enough to georeference, and which fields are missing. Never guesses — missing stays missing. """ try: from src.geolocation.telemetry import parse_telemetry except ImportError as exc: raise HTTPException(status_code=503, detail="Pillow not installed.") from exc suffix = Path(file.filename or "frame.jpg").suffix or ".jpg" with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: shutil.copyfileobj(file.file, tmp) tmp_path = tmp.name try: tel = parse_telemetry(tmp_path) finally: Path(tmp_path).unlink(missing_ok=True) return { "source": tel.source, # "xmp", "exif", "xmp+exif", "none" "viewpoint": tel.viewpoint, # "nadir" | "oblique" | None "has_geometry": tel.to_camera() is not None, "missing": tel.missing(), "altitude_m": tel.altitude_m, "gimbal_pitch_deg": tel.gimbal_pitch_deg, "cam_pitch_deg": tel.cam_pitch_deg, "gimbal_yaw_deg": tel.gimbal_yaw_deg, "hfov_deg": tel.hfov_deg, "focal_35mm": tel.focal_35mm, "gps_lat": tel.gps_lat, "gps_lon": tel.gps_lon, "image_width": tel.image_width, "image_height": tel.image_height, }