# Common Helper — `WgsConverter`

## Purpose

WGS84 ↔ local tangent-plane (ENU/NED) ↔ tile pixel-coordinate conversions. Required by every component that interacts with geographic positions — from C4's pose estimation, through C5's state graph, through C6's tile-bounding-box queries, through C8's per-FC encoding, through C10's bbox provisioning, through C12's operator UX.

## Used By

- C4 — Pose Estimation.
- C5 — State Estimator.
- C6 — Tile Cache + Spatial Index (bbox queries).
- C8 — FC Adapter (per-FC encoding of LatLonAlt → MAVLink/MSP2).
- C10 — Pre-flight Cache Provisioning (bbox → tile-id list).
- C12 — Operator Pre-flight Tooling (operator-entered bbox).

## Interface (sketch)

```
class WgsConverter:
    @staticmethod
    def latlonalt_to_ecef(p: LatLonAlt) -> Vector3
    @staticmethod
    def ecef_to_latlonalt(p: Vector3) -> LatLonAlt
    @staticmethod
    def latlonalt_to_local_enu(origin: LatLonAlt, p: LatLonAlt) -> Vector3
    @staticmethod
    def local_enu_to_latlonalt(origin: LatLonAlt, p_enu: Vector3) -> LatLonAlt
    @staticmethod
    def latlon_to_tile_xy(zoom: int, lat: float, lon: float) -> tuple[int, int]
    @staticmethod
    def tile_xy_to_latlon_bounds(zoom: int, x: int, y: int) -> BoundingBox
```

## Implementation Notes

- Stateless; pure functions.
- Backed by `pyproj` for the geodesy primitives; tile_xy math uses the standard slippy-map convention (matches `satellite-provider`'s on-disk layout).
- All conversions use WGS84 ellipsoid; no datum-shift complexity.

## Caveats

- The static-only design satisfies the coderule.mdc constraint ("only use static methods for pure self-contained computations"). If a future deployment needs alternative datum support, switch to an instance-based factory then.
- Tile-coordinate math is zoom-level-sensitive; callers MUST pass the right zoom level for the tile in question (typically zoomLevel from `TileMetadata`).
