avwx.parsing.translate.taf

TAF data translation handlers.

  1"""TAF data translation handlers."""
  2
  3# stdlib
  4from __future__ import annotations
  5
  6# module
  7import avwx.parsing.translate.base as _trans
  8from avwx.parsing import core
  9from avwx.parsing.translate import remarks
 10from avwx.static.taf import ICING_CONDITIONS, TURBULENCE_CONDITIONS
 11from avwx.structs import TafData, TafLineTrans, TafTrans, Units
 12
 13
 14def wind_shear(
 15    shear: str | None,
 16    unit_alt: str = "ft",
 17    unit_wind: str = "kt",
 18    *,
 19    spoken: bool = False,
 20) -> str:
 21    """Translate wind shear into a readable string.
 22
 23    Ex: Wind shear 2000ft from 140 at 30kt
 24    """
 25    if not shear or "WS" not in shear or "/" not in shear:
 26        return ""
 27    altitude, wind = shear[2:].rstrip(unit_wind.upper()).split("/")
 28    wdir = core.spoken_number(wind[:3], literal=True) if spoken else wind[:3]
 29    return f"Wind shear {int(altitude)*100}{unit_alt} from {wdir} at {wind[3:]}{unit_wind}"
 30
 31
 32def turb_ice(values: list[str], unit: str = "ft") -> str:
 33    """Translate the list of turbulence or icing into a readable sentence.
 34
 35    Ex: Occasional moderate turbulence in clouds from 3000ft to 14000ft
 36    """
 37    if not values:
 38        return ""
 39    # Determine turbulence or icing
 40    if values[0][0] == "5":
 41        conditions = TURBULENCE_CONDITIONS
 42    elif values[0][0] == "6":
 43        conditions = ICING_CONDITIONS
 44    else:
 45        return ""
 46    # Create list of split items (type, floor, height)
 47    split = [[item[1:2], item[2:5], item[5]] for item in values if len(item) == 6]
 48    # Combine items that cover a layer greater than 9000ft
 49    for i in reversed(range(len(split) - 1)):
 50        if (
 51            split[i][2] == "9"
 52            and split[i][0] == split[i + 1][0]
 53            and int(split[i + 1][1]) == (int(split[i][1]) + int(split[i][2]) * 10)
 54        ):
 55            split[i][2] = str(int(split[i][2]) + int(split[i + 1][2]))
 56            split.pop(i + 1)
 57    # Return joined, formatted string from split items
 58    return ", ".join(
 59        f"{conditions[item[0]]} from {int(item[1]) * 100}{unit} to {int(item[1]) * 100 + int(item[2]) * 1000}{unit}"
 60        for item in split
 61    )
 62
 63
 64def min_max_temp(temp: str | None, unit: str = "C") -> str:
 65    """Format the Min and Max temp elements into a readable string.
 66
 67    Ex: Maximum temperature of 23°C (73°F) at 18-15:00Z
 68    """
 69    if not temp or len(temp) < 7:
 70        return ""
 71    if temp[:2] == "TX":
 72        temp_type = "Maximum"
 73    elif temp[:2] == "TN":
 74        temp_type = "Minimum"
 75    else:
 76        return ""
 77    value, time = temp[2:].replace("M", "-").replace("Z", "").replace("//", "/").strip("/").split("/")
 78    if len(time) > 2:
 79        time = f"{time[:2]}-{time[2:]}"
 80    translation = _trans.temperature(core.make_number(value), unit)
 81    return f"{temp_type} temperature of {translation} at {time}:00Z"
 82
 83
 84def translate_taf(wxdata: TafData, units: Units) -> TafTrans:
 85    """Return translations for a TafData object."""
 86    forecast: list[TafLineTrans] = []
 87    for line in wxdata.forecast:
 88        shared = _trans.current_shared(line, units)
 89        # Remove false 'Sky Clear' if line type is 'BECMG'
 90        clouds = shared.clouds
 91        if line.type == "BECMG" and clouds == "Sky clear":
 92            clouds = ""
 93        struct = TafLineTrans(
 94            altimeter=shared.altimeter,
 95            clouds=clouds,
 96            wx_codes=shared.wx_codes,
 97            visibility=shared.visibility,
 98            wind=_trans.wind(
 99                line.wind_direction,
100                line.wind_speed,
101                line.wind_gust,
102                line.wind_variable_direction,
103                unit=units.wind_speed,
104            ),
105            wind_shear=wind_shear(line.wind_shear, units.altitude, units.wind_speed),
106            turbulence=turb_ice(line.turbulence, units.altitude),
107            icing=turb_ice(line.icing, units.altitude),
108        )
109        forecast.append(struct)
110    return TafTrans(
111        forecast=forecast,
112        max_temp=min_max_temp(wxdata.max_temp, units.temperature),
113        min_temp=min_max_temp(wxdata.min_temp, units.temperature),
114        remarks=remarks.translate(wxdata.remarks, wxdata.remarks_info),
115    )
def wind_shear( shear: str | None, unit_alt: str = 'ft', unit_wind: str = 'kt', *, spoken: bool = False) -> str:
15def wind_shear(
16    shear: str | None,
17    unit_alt: str = "ft",
18    unit_wind: str = "kt",
19    *,
20    spoken: bool = False,
21) -> str:
22    """Translate wind shear into a readable string.
23
24    Ex: Wind shear 2000ft from 140 at 30kt
25    """
26    if not shear or "WS" not in shear or "/" not in shear:
27        return ""
28    altitude, wind = shear[2:].rstrip(unit_wind.upper()).split("/")
29    wdir = core.spoken_number(wind[:3], literal=True) if spoken else wind[:3]
30    return f"Wind shear {int(altitude)*100}{unit_alt} from {wdir} at {wind[3:]}{unit_wind}"

Translate wind shear into a readable string.

Ex: Wind shear 2000ft from 140 at 30kt

def turb_ice(values: list[str], unit: str = 'ft') -> str:
33def turb_ice(values: list[str], unit: str = "ft") -> str:
34    """Translate the list of turbulence or icing into a readable sentence.
35
36    Ex: Occasional moderate turbulence in clouds from 3000ft to 14000ft
37    """
38    if not values:
39        return ""
40    # Determine turbulence or icing
41    if values[0][0] == "5":
42        conditions = TURBULENCE_CONDITIONS
43    elif values[0][0] == "6":
44        conditions = ICING_CONDITIONS
45    else:
46        return ""
47    # Create list of split items (type, floor, height)
48    split = [[item[1:2], item[2:5], item[5]] for item in values if len(item) == 6]
49    # Combine items that cover a layer greater than 9000ft
50    for i in reversed(range(len(split) - 1)):
51        if (
52            split[i][2] == "9"
53            and split[i][0] == split[i + 1][0]
54            and int(split[i + 1][1]) == (int(split[i][1]) + int(split[i][2]) * 10)
55        ):
56            split[i][2] = str(int(split[i][2]) + int(split[i + 1][2]))
57            split.pop(i + 1)
58    # Return joined, formatted string from split items
59    return ", ".join(
60        f"{conditions[item[0]]} from {int(item[1]) * 100}{unit} to {int(item[1]) * 100 + int(item[2]) * 1000}{unit}"
61        for item in split
62    )

Translate the list of turbulence or icing into a readable sentence.

Ex: Occasional moderate turbulence in clouds from 3000ft to 14000ft

def min_max_temp(temp: str | None, unit: str = 'C') -> str:
65def min_max_temp(temp: str | None, unit: str = "C") -> str:
66    """Format the Min and Max temp elements into a readable string.
67
68    Ex: Maximum temperature of 23°C (73°F) at 18-15:00Z
69    """
70    if not temp or len(temp) < 7:
71        return ""
72    if temp[:2] == "TX":
73        temp_type = "Maximum"
74    elif temp[:2] == "TN":
75        temp_type = "Minimum"
76    else:
77        return ""
78    value, time = temp[2:].replace("M", "-").replace("Z", "").replace("//", "/").strip("/").split("/")
79    if len(time) > 2:
80        time = f"{time[:2]}-{time[2:]}"
81    translation = _trans.temperature(core.make_number(value), unit)
82    return f"{temp_type} temperature of {translation} at {time}:00Z"

Format the Min and Max temp elements into a readable string.

Ex: Maximum temperature of 23°C (73°F) at 18-15:00Z

def translate_taf( wxdata: avwx.structs.TafData, units: avwx.structs.Units) -> avwx.structs.TafTrans:
 85def translate_taf(wxdata: TafData, units: Units) -> TafTrans:
 86    """Return translations for a TafData object."""
 87    forecast: list[TafLineTrans] = []
 88    for line in wxdata.forecast:
 89        shared = _trans.current_shared(line, units)
 90        # Remove false 'Sky Clear' if line type is 'BECMG'
 91        clouds = shared.clouds
 92        if line.type == "BECMG" and clouds == "Sky clear":
 93            clouds = ""
 94        struct = TafLineTrans(
 95            altimeter=shared.altimeter,
 96            clouds=clouds,
 97            wx_codes=shared.wx_codes,
 98            visibility=shared.visibility,
 99            wind=_trans.wind(
100                line.wind_direction,
101                line.wind_speed,
102                line.wind_gust,
103                line.wind_variable_direction,
104                unit=units.wind_speed,
105            ),
106            wind_shear=wind_shear(line.wind_shear, units.altitude, units.wind_speed),
107            turbulence=turb_ice(line.turbulence, units.altitude),
108            icing=turb_ice(line.icing, units.altitude),
109        )
110        forecast.append(struct)
111    return TafTrans(
112        forecast=forecast,
113        max_temp=min_max_temp(wxdata.max_temp, units.temperature),
114        min_temp=min_max_temp(wxdata.min_temp, units.temperature),
115        remarks=remarks.translate(wxdata.remarks, wxdata.remarks_info),
116    )

Return translations for a TafData object.