DataFrame methods to parse the sky condition from a terminal aerodrome forecast.
A line in a taf can report zero-eight cloud layers. Cloud layers are required in predominate lines, and optional in temporary ones. Cloud cover SKC|FEW|SCT|BKN|OVC
is associated to an octave value. 1, 3, 5, 8
as the min sky coverage for reporting a layer.
I struggled to find a pure regex solution to generate the the pattern I needed for repeating capture groups. Hence the _unpack_setup
function
from typing import Iterable
import re
import pandas as pd
import numpy as np
TAF = """
KGCC 282320Z 2900/2924 09010KT P6SM -SHRA BKN070 OVC250
FM290300 24011KT P6SM OVC040
TEMPO 2903/2906 4SM -SHRA FEW010 FEW015 BKN020TCU OVC025
FM291000 18009KT 3SM -TSRA BR OVC004CB
FM291900 31022G33KT 6SM -SHRA OVC011
"""
OCTAVE_INDEX = pd.Series(
(np.inf, 1, 3, 5, 8, np.nan), index=["SKC", "FEW", "SCT", "BKN", "OVC", np.nan]
)
def _unpack_setup():
base = r"(SKC|FEW|SCT|BKN|OVC)(\d{3})?(CB|TCU)?\s?"
layers = f"(?:{base})?" * 7
columns = pd.Series(["CloudCover", "CloudBase", "Flags"])
return (
re.compile(base + layers, re.VERBOSE),
pd.concat(columns + str(i) for i in range(1, 9)),
)
celestial_dome, cloud_columns = _unpack_setup()
def unpack_index(index: pd.Index, *args: str) -> Iterable[pd.Index]:
for col in args:
yield index[index.str.contains(col)]
def octave(sky_coverage: pd.Series) -> np.ndarray:
"""octave indexer"""
return OCTAVE_INDEX[sky_coverage].values
def get_sky_condition():
"""creates sky condtion dataframe"""
series = pd.Series(re.split(r"(?:\s(?=BECMG|TEMPO|FM))", TAF.strip())).str.strip()
sky_condition: pd.DataFrame = (
series.str.extract(celestial_dome)
.set_axis(cloud_columns, axis=1)
.dropna(axis=1, how="all")
)
column_base, column_cover = unpack_index(
sky_condition.columns, "CloudBase", "CloudCover"
)
sky_condition[column_base] = sky_condition[column_base].astype(float) * 100
sky_condition[column_cover] = sky_condition[column_cover].apply(octave)
print(sky_condition)
if __name__ == "__main__":
get_sky_condition()
results
CloudCover1 CloudBase1 Flags1 CloudCover2 CloudBase2 CloudCover3 CloudBase3 Flags3 CloudCover4 CloudBase4
0 5.0 7000.0 NaN 8.0 25000.0 NaN NaN NaN NaN NaN
1 8.0 4000.0 NaN NaN NaN NaN NaN NaN NaN NaN
2 2.0 1000.0 NaN 2.0 1500.0 5.0 2000.0 TCU 8.0 2500.0
3 8.0 400.0 CB NaN NaN NaN NaN NaN NaN NaN
4 8.0 1100.0 NaN NaN NaN NaN NaN NaN NaN NaN
TEMPO 2903/2906 5000 TSRA
\$\endgroup\$KT
. Otherwise, what is it? \$\endgroup\$5000
may have been confusing as the example uses statue miles for visibility and I used a meter example. In the application I'm developing all values get converted to a standard unit. \$\endgroup\$