avwx.flight_path

Methods to resolve flight paths in coordinates

  1"""
  2Methods to resolve flight paths in coordinates
  3"""
  4
  5# stdlib
  6from typing import List, Optional, Union
  7
  8# library
  9from geopy.distance import great_circle  # type: ignore
 10
 11# module
 12from avwx.exceptions import BadStation
 13from avwx.load_utils import LazyLoad
 14from avwx.station import Station
 15from avwx.structs import Coord
 16
 17NAVAIDS = LazyLoad("navaids")
 18QCoord = Union[Coord, List[Coord]]
 19
 20
 21def _distance(near: Coord, far: Coord) -> float:
 22    circle = great_circle(near.pair, far.pair).nm
 23    if not isinstance(circle, float):
 24        raise ValueError("Could not evaluate great circle distance")
 25    return circle
 26
 27
 28def _closest(coord: QCoord, coords: List[Coord]) -> Coord:
 29    if isinstance(coord, Coord):
 30        distances = [(_distance(coord, c), c) for c in coords]
 31    else:
 32        distances = [(_distance(c, _closest(c, coords)), c) for c in coord]
 33    distances.sort(key=lambda x: x[0])
 34    return distances[0][1]
 35
 36
 37def _best_coord(
 38    previous: Optional[QCoord],
 39    current: QCoord,
 40    up_next: Optional[QCoord],
 41) -> Coord:
 42    """Determine the best coordinate based on surroundings
 43    At least one of these should be a list
 44    """
 45    if previous is None and up_next is None:
 46        if isinstance(current, list):
 47            raise ValueError("Unable to determine best coordinate")
 48        return current
 49    # NOTE: add handling to determine best midpoint
 50    if up_next is None:
 51        up_next = previous
 52    if isinstance(up_next, list):
 53        return _closest(current, up_next)
 54    return _closest(up_next, current)  # type: ignore
 55
 56
 57def _to_coordinates(
 58    values: List[Union[Coord, str]], last_value: Optional[QCoord] = None
 59) -> List[Coord]:
 60    if not values:
 61        return []
 62    coord = values[0]
 63    if isinstance(coord, str):
 64        coord = coord.strip()
 65        try:
 66            value = coord
 67            coord = Station.from_icao(coord).coord
 68            coord.repr = value
 69        except BadStation:
 70            try:
 71                coords = [Coord(lat=c[0], lon=c[1], repr=coord) for c in NAVAIDS[coord]]  # type: ignore
 72            except KeyError:
 73                value = coord  # type: ignore
 74                coord = Station.from_code(coord).coord  # type: ignore
 75                coord.repr = value
 76            else:
 77                if len(coords) == 1:
 78                    coord = coords[0]
 79                else:
 80                    new_coords = _to_coordinates(values[1:], coords)
 81                    new_coord = new_coords[0] if new_coords else None
 82                    coord = _best_coord(last_value, coords, new_coord)
 83                    return [coord] + new_coords
 84    return [coord] + _to_coordinates(values[1:], coord)
 85
 86
 87def to_coordinates(values: List[Union[Coord, str]]) -> List[Coord]:
 88    """Convert any known idents found in a flight path into coordinates
 89
 90    Prefers Coord > ICAO > Navaid > IATA / GPS
 91    """
 92    if not values:
 93        return []
 94    cleaned = []
 95    for value in values:
 96        if not value:
 97            continue
 98        if isinstance(value, str):
 99            value = value.strip()
100            if not value:
101                continue
102        cleaned.append(value)
103    return _to_coordinates(values)
QCoord = typing.Union[avwx.structs.Coord, typing.List[avwx.structs.Coord]]
def to_coordinates(values: List[Union[avwx.structs.Coord, str]]) -> List[avwx.structs.Coord]:
 88def to_coordinates(values: List[Union[Coord, str]]) -> List[Coord]:
 89    """Convert any known idents found in a flight path into coordinates
 90
 91    Prefers Coord > ICAO > Navaid > IATA / GPS
 92    """
 93    if not values:
 94        return []
 95    cleaned = []
 96    for value in values:
 97        if not value:
 98            continue
 99        if isinstance(value, str):
100            value = value.strip()
101            if not value:
102                continue
103        cleaned.append(value)
104    return _to_coordinates(values)

Convert any known idents found in a flight path into coordinates

Prefers Coord > ICAO > Navaid > IATA / GPS