def get_wind( data: List[str], units: Units ) -> Tuple[List[str], Number, Number, Number, List[Number]]: """Returns the report list and removed: Direction string, speed string, gust string, variable direction list """ direction, speed, gust = "", "", "" variable = [] if data: item = copy(data[0]) for rep in ["(E)"]: item = item.replace(rep, "") for replacements in (("O", "0"), ("/", ""), ("LKT", "KT"), ("GG", "G")): item = item.replace(*replacements) # 09010KT, 09010G15KT if (item.endswith("KT") or item.endswith("KTS") or item.endswith("MPS") or item.endswith("KMH") or ((len(item) == 5 or (len(item) >= 8 and item.find("G") != -1) and item.find("/") == -1) and (item[:5].isdigit() or (item.startswith("VRB") and item[3:5].isdigit())))): # In order of frequency if item.endswith("KT"): item = item.replace("KT", "") elif item.endswith("KTS"): item = item.replace("KTS", "") elif item.endswith("MPS"): units.wind_speed = "m/s" item = item.replace("MPS", "") elif item.endswith("KMH"): units.wind_speed = "km/h" item = item.replace("KMH", "") direction = item[:3] if "G" in item: g_index = item.find("G") gust = item[g_index + 1:] speed = item[3:g_index] else: speed = item[3:] direction = item[:3] data.pop(0) # Separated Gust if data and 1 < len( data[0]) < 4 and data[0][0] == "G" and data[0][1:].isdigit(): gust = data.pop(0)[1:] # Variable Wind Direction if (data and len(data[0]) == 7 and data[0][:3].isdigit() and data[0][3] == "V" and data[0][4:].isdigit()): variable = [ make_number(i, speak=i, literal=True) for i in data.pop(0).split("V") ] # Convert to Number direction = make_number(direction, speak=direction, literal=True) speed = make_number(speed.strip("BV")) gust = make_number(gust) return data, direction, speed, gust, variable
def get_altimeter(wxdata: [str], units: Units, version: str = "NA") -> ([str], Number): """ Returns the report list and the removed altimeter item Version is 'NA' (North American / default) or 'IN' (International) """ if not wxdata: return wxdata, None altimeter = "" target = wxdata[-1] # Handle QNH prefix: buf = 1 if target.startswith("QNH"): buf = 3 target = target.replace("QNH", "Q") if version == "NA": # Version target if target[0] == "A": altimeter = wxdata.pop()[buf:] # Other version but prefer normal if available elif target[0] == "Q": if len(wxdata) > 1 and wxdata[-2][0] == "A": wxdata.pop() altimeter = wxdata.pop()[buf:] else: units.altimeter = "hPa" altimeter = wxdata.pop()[buf:].lstrip(".") # Else grab the digits elif len(target) == 4 and target.isdigit(): altimeter = wxdata.pop() elif version == "IN": # Version target if target[0] == "Q": altimeter = wxdata.pop()[buf:].lstrip(".") if "/" in altimeter: altimeter = altimeter[:altimeter.find("/")] # Other version but prefer normal if available elif target[0] == "A": if len(wxdata) > 1 and wxdata[-2][0] == "Q": wxdata.pop() altimeter = wxdata.pop()[buf:] else: units.altimeter = "inHg" altimeter = wxdata.pop()[buf:] # Some stations report both, but we only need one if wxdata and (wxdata[-1][0] == "A" or wxdata[-1][0] == "Q"): wxdata.pop() # convert to Number altimeter = altimeter.replace("/", "") if not altimeter: return wxdata, None if units.altimeter == "inHg": value = altimeter[:2] + "." + altimeter[2:] else: value = altimeter return wxdata, make_number(value, altimeter)
def get_wind(wxdata: [str], units: Units) -> ([str], Number, Number, Number, [Number]): """ Returns the report list and removed: Direction string, speed string, gust string, variable direction list """ direction, speed, gust = '', '', '' variable = [] if wxdata: item = copy(wxdata[0]) for rep in ['(E)']: item = item.replace(rep, '') for replacements in ( ('O', '0'), ('/', ''), ): item = item.replace(*replacements) #09010KT, 09010G15KT if item.endswith('KT') \ or item.endswith('KTS') \ or item.endswith('MPS') \ or item.endswith('KMH') \ or ((len(item) == 5 or (len(item) >= 8 and item.find('G') != -1) and item.find('/') == -1) and (item[:5].isdigit() or (item.startswith('VRB') and item[3:5].isdigit()))): #In order of frequency if item.endswith('KT'): item = item.replace('KT', '') elif item.endswith('KTS'): item = item.replace('KTS', '') elif item.endswith('MPS'): units.wind_speed = 'm/s' item = item.replace('MPS', '') elif item.endswith('KMH'): units.wind_speed = 'km/h' item = item.replace('KMH', '') direction = item[:3] if 'G' in item: g_index = item.find('G') gust = item[g_index + 1:] speed = item[3:g_index] else: speed = item[3:] wxdata.pop(0) #Separated Gust if wxdata and 1 < len( wxdata[0]) < 4 and wxdata[0][0] == 'G' and wxdata[0][1:].isdigit(): gust = wxdata.pop(0)[1:] #Variable Wind Direction if wxdata and len(wxdata[0]) == 7 and wxdata[0][:3].isdigit() \ and wxdata[0][3] == 'V' and wxdata[0][4:].isdigit(): variable = [make_number(i, speak=i) for i in wxdata.pop(0).split('V')] # Convert to Number direction = make_number(direction, speak=direction) speed = make_number(speed) gust = make_number(gust) return wxdata, direction, speed, gust, variable
def parse(station: str, report: str) -> (TafData, Units): """ Returns TafData and Units dataclasses with parsed data and their associated units """ if not report: return None, None valid_station(station) while len(report) > 3 and report[:4] in ("TAF ", "AMD ", "COR "): report = report[4:] retwx = { "end_time": None, "raw": report, "remarks": None, "start_time": None } report = _core.sanitize_report_string(report) _, station, time = _core.get_station_and_time(report[:20].split()) retwx["station"] = station retwx["time"] = _core.make_timestamp(time) report = report.replace(station, "") if time: report = report.replace(time, "").strip() if uses_na_format(station): use_na = True units = Units(**NA_UNITS) else: use_na = False units = Units(**IN_UNITS) # Find and remove remarks report, retwx["remarks"] = _core.get_taf_remarks(report) # Split and parse each line lines = _core.split_taf(report) parsed_lines = parse_lines(lines, units, use_na) # Perform additional info extract and corrections if parsed_lines: parsed_lines[-1]["other"], retwx["max_temp"], retwx[ "min_temp"] = _core.get_temp_min_and_max(parsed_lines[-1]["other"]) if not (retwx["max_temp"] or retwx["min_temp"]): parsed_lines[0]["other"], retwx["max_temp"], retwx[ "min_temp"] = _core.get_temp_min_and_max( parsed_lines[0]["other"]) # Set start and end times based on the first line start, end = parsed_lines[0]["start_time"], parsed_lines[0]["end_time"] parsed_lines[0]["end_time"] = None retwx["start_time"], retwx["end_time"] = start, end parsed_lines = _core.find_missing_taf_times(parsed_lines, start, end) parsed_lines = _core.get_taf_flight_rules(parsed_lines) # Extract Oceania-specific data if retwx["station"][0] == "A": parsed_lines[-1]["other"], retwx["alts"], retwx[ "temps"] = _core.get_oceania_temp_and_alt( parsed_lines[-1]["other"]) # Convert to dataclass retwx["forecast"] = [TafLineData(**line) for line in parsed_lines] return TafData(**retwx), units
def get_wind(wxdata: [str], units: Units) -> ([str], Number, Number, Number, [Number]): """ Returns the report list and removed: Direction string, speed string, gust string, variable direction list """ direction, speed, gust = '', '', '' variable = [] if wxdata: item = copy(wxdata[0]) for rep in ['(E)']: item = item.replace(rep, '') for replacements in ( ('O', '0'), ('/', ''), ): item = item.replace(*replacements) #09010KT, 09010G15KT if item.endswith('KT') \ or item.endswith('KTS') \ or item.endswith('MPS') \ or item.endswith('KMH') \ or ((len(item) == 5 or (len(item) >= 8 and item.find('G') != -1) and item.find('/') == -1) and (item[:5].isdigit() or (item.startswith('VRB') and item[3:5].isdigit()))): #In order of frequency if item.endswith('KT'): item = item.replace('KT', '') elif item.endswith('KTS'): item = item.replace('KTS', '') elif item.endswith('MPS'): units.wind_speed = 'm/s' item = item.replace('MPS', '') elif item.endswith('KMH'): units.wind_speed = 'km/h' item = item.replace('KMH', '') direction = item[:3] if 'G' in item: g_index = item.find('G') gust = item[g_index + 1:] speed = item[3:g_index] else: speed = item[3:] wxdata.pop(0) #Separated Gust if wxdata and 1 < len(wxdata[0]) < 4 and wxdata[0][0] == 'G' and wxdata[0][1:].isdigit(): gust = wxdata.pop(0)[1:] #Variable Wind Direction if wxdata and len(wxdata[0]) == 7 and wxdata[0][:3].isdigit() \ and wxdata[0][3] == 'V' and wxdata[0][4:].isdigit(): variable = [make_number(i, speak=i) for i in wxdata.pop(0).split('V')] # Convert to Number direction = make_number(direction, speak=direction) speed = make_number(speed) gust = make_number(gust) return wxdata, direction, speed, gust, variable
def get_altimeter(wxdata: [str], units: Units, version: str = 'NA') -> ([str], Number): """ Returns the report list and the removed altimeter item Version is 'NA' (North American / default) or 'IN' (International) """ if not wxdata: return wxdata, None altimeter = '' target = wxdata[-1] if version == 'NA': # Version target if target[0] == 'A': altimeter = wxdata.pop()[1:] # Other version but prefer normal if available elif target[0] == 'Q': if len(wxdata) > 1 and wxdata[-2][0] == 'A': wxdata.pop() altimeter = wxdata.pop()[1:] else: units.altimeter = 'hPa' altimeter = wxdata.pop()[1:].lstrip('.') # Else grab the digits elif len(target) == 4 and target.isdigit(): altimeter = wxdata.pop() elif version == 'IN': # Version target if target[0] == 'Q': altimeter = wxdata.pop()[1:].lstrip('.') if '/' in altimeter: altimeter = altimeter[:altimeter.find('/')] # Other version but prefer normal if available elif target[0] == 'A': if len(wxdata) > 1 and wxdata[-2][0] == 'Q': wxdata.pop() altimeter = wxdata.pop()[1:] else: units.altimeter = 'inHg' altimeter = wxdata.pop()[1:] #Some stations report both, but we only need one if wxdata and (wxdata[-1][0] == 'A' or wxdata[-1][0] == 'Q'): wxdata.pop() # convert to Number if not altimeter: return wxdata, None altimeter = altimeter.replace('/', '') if units.altimeter == 'inHg': value = altimeter[:2] + '.' + altimeter[2:] else: value = altimeter return wxdata, make_number(value, altimeter)
def parse(station: str, report: str) -> TafData: """ Returns TafData and Units dataclasses with parsed data and their associated units """ if not report: return None, None core.valid_station(station) while len(report) > 3 and report[:4] in ('TAF ', 'AMD ', 'COR '): report = report[4:] _, station, time = core.get_station_and_time(report[:20].split()) retwx = { 'end_time': None, 'raw': report, 'remarks': None, 'start_time': None, 'station': station, 'time': core.make_timestamp(time) } report = report.replace(station, '') report = report.replace(time, '').strip() if core.uses_na_format(station): use_na = True units = Units(**NA_UNITS) else: use_na = False units = Units(**IN_UNITS) # Find and remove remarks report, retwx['remarks'] = core.get_taf_remarks(report) # Split and parse each line lines = core.split_taf(report) parsed_lines = parse_lines(lines, units, use_na) # Perform additional info extract and corrections if parsed_lines: parsed_lines[-1]['other'], retwx['max_temp'], retwx['min_temp'] \ = core.get_temp_min_and_max(parsed_lines[-1]['other']) if not (retwx['max_temp'] or retwx['min_temp']): parsed_lines[0]['other'], retwx['max_temp'], retwx['min_temp'] \ = core.get_temp_min_and_max(parsed_lines[0]['other']) # Set start and end times based on the first line start, end = parsed_lines[0]['start_time'], parsed_lines[0]['end_time'] parsed_lines[0]['end_time'] = None retwx['start_time'], retwx['end_time'] = start, end parsed_lines = core.find_missing_taf_times(parsed_lines, start, end) parsed_lines = core.get_taf_flight_rules(parsed_lines) # Extract Oceania-specific data if retwx['station'][0] == 'A': parsed_lines[-1]['other'], retwx['alts'], retwx['temps'] \ = core.get_oceania_temp_and_alt(parsed_lines[-1]['other']) # Convert to dataclass retwx['forecast'] = [TafLineData(**line) for line in parsed_lines] return TafData(**retwx), units
def parse_in(txt: str) -> (MetarData, Units): """ Parser for the International METAR variant """ units = Units(**IN_UNITS) clean = core.sanitize_report_string(txt) wxresp = {'raw': txt, 'sanitized': clean} wxdata, wxresp['remarks'] = core.get_remarks(clean) wxdata, wxresp['runway_visibility'], _ = core.sanitize_report_list(wxdata) wxdata, wxresp['station'], wxresp['time'] = core.get_station_and_time( wxdata) if 'CAVOK' not in wxdata: wxdata, wxresp['clouds'] = core.get_clouds(wxdata) wxdata, wxresp['wind_direction'], wxresp['wind_speed'], \ wxresp['wind_gust'], wxresp['wind_variable_direction'] = core.get_wind(wxdata, units) wxdata, wxresp['altimeter'] = core.get_altimeter(wxdata, units, 'IN') if 'CAVOK' in wxdata: wxresp['visibility'] = core.make_number('CAVOK') wxresp['clouds'] = [] wxdata.remove('CAVOK') else: wxdata, wxresp['visibility'] = core.get_visibility(wxdata, units) wxresp['other'], wxresp['temperature'], wxresp[ 'dewpoint'] = core.get_temp_and_dew(wxdata) condition = core.get_flight_rules(wxresp['visibility'], core.get_ceiling(wxresp['clouds'])) wxresp['flight_rules'] = FLIGHT_RULES[condition] wxresp['remarks_info'] = remarks.parse(wxresp['remarks']) wxresp['time'] = core.make_timestamp(wxresp['time']) return MetarData(**wxresp), units
def parse_in(report: str) -> (MetarData, Units): """ Parser for the International METAR variant """ units = Units(**IN_UNITS) wxresp = {"raw": report} clean = _core.sanitize_report_string(report) wxdata, wxresp["remarks"] = _core.get_remarks(clean) wxdata = _core.dedupe(wxdata) wxdata = _core.sanitize_report_list(wxdata) wxresp["sanitized"] = " ".join(wxdata + [wxresp["remarks"]]) wxdata, wxresp["station"], wxresp["time"] = _core.get_station_and_time(wxdata) wxdata, wxresp["runway_visibility"] = _core.get_runway_visibility(wxdata) if "CAVOK" not in wxdata: wxdata, wxresp["clouds"] = _core.get_clouds(wxdata) wxdata, wxresp["wind_direction"], wxresp["wind_speed"], wxresp["wind_gust"], wxresp[ "wind_variable_direction" ] = _core.get_wind(wxdata, units) wxdata, wxresp["altimeter"] = _core.get_altimeter(wxdata, units, "IN") if "CAVOK" in wxdata: wxresp["visibility"] = _core.make_number("CAVOK") wxresp["clouds"] = [] wxdata.remove("CAVOK") else: wxdata, wxresp["visibility"] = _core.get_visibility(wxdata, units) wxresp["other"], wxresp["temperature"], wxresp["dewpoint"] = _core.get_temp_and_dew( wxdata ) condition = _core.get_flight_rules( wxresp["visibility"], _core.get_ceiling(wxresp["clouds"]) ) wxresp["flight_rules"] = FLIGHT_RULES[condition] wxresp["remarks_info"] = remarks.parse(wxresp["remarks"]) wxresp["time"] = _core.make_timestamp(wxresp["time"]) return MetarData(**wxresp), units
def parse_na(report: str) -> (MetarData, Units): """ Parser for the North American METAR variant """ units = Units(**NA_UNITS) wxresp = {"raw": report} clean = core.sanitize_report_string(report) wxdata, wxresp["remarks"] = get_remarks(clean) wxdata = core.dedupe(wxdata) wxdata = core.sanitize_report_list(wxdata) wxresp["sanitized"] = " ".join(wxdata + [wxresp["remarks"]]) wxdata, wxresp["station"], wxresp["time"] = core.get_station_and_time( wxdata) wxdata, wxresp["runway_visibility"] = get_runway_visibility(wxdata) wxdata, wxresp["clouds"] = core.get_clouds(wxdata) ( wxdata, wxresp["wind_direction"], wxresp["wind_speed"], wxresp["wind_gust"], wxresp["wind_variable_direction"], ) = core.get_wind(wxdata, units) wxdata, wxresp["altimeter"] = get_altimeter(wxdata, units, "NA") wxdata, wxresp["visibility"] = core.get_visibility(wxdata, units) wxdata, wxresp["temperature"], wxresp["dewpoint"] = get_temp_and_dew( wxdata) condition = core.get_flight_rules(wxresp["visibility"], core.get_ceiling(wxresp["clouds"])) wxresp["other"], wxresp["wx_codes"] = get_wx_codes(wxdata) wxresp["flight_rules"] = FLIGHT_RULES[condition] wxresp["remarks_info"] = remarks.parse(wxresp["remarks"]) wxresp["time"] = core.make_timestamp(wxresp["time"]) return MetarData(**wxresp), units
def parse_in(report: str, issued: date = None) -> (MetarData, Units): """ Parser for the International METAR variant """ units = Units(**IN_UNITS) resp = {"raw": report} resp["sanitized"], resp["remarks"], data = sanitize(report) data, resp["station"], resp["time"] = core.get_station_and_time(data) data, resp["runway_visibility"] = get_runway_visibility(data) if "CAVOK" not in data: data, resp["clouds"] = core.get_clouds(data) ( data, resp["wind_direction"], resp["wind_speed"], resp["wind_gust"], resp["wind_variable_direction"], ) = core.get_wind(data, units) data, resp["altimeter"] = get_altimeter(data, units, "IN") if "CAVOK" in data: resp["visibility"] = core.make_number("CAVOK") resp["clouds"] = [] data.remove("CAVOK") else: data, resp["visibility"] = core.get_visibility(data, units) data, resp["temperature"], resp["dewpoint"] = get_temp_and_dew(data) condition = core.get_flight_rules(resp["visibility"], core.get_ceiling(resp["clouds"])) resp["other"], resp["wx_codes"] = get_wx_codes(data) resp["flight_rules"] = FLIGHT_RULES[condition] resp["remarks_info"] = remarks.parse(resp["remarks"]) resp["time"] = core.make_timestamp(resp["time"], target_date=issued) return MetarData(**resp), units
def parse_na(report: str) -> (MetarData, Units): """ Parser for the North American METAR variant """ units = Units(**NA_UNITS) clean = core.sanitize_report_string(report) wxresp = {'raw': report, 'sanitized': clean} wxdata, wxresp['remarks'] = core.get_remarks(clean) wxdata = core.dedupe(wxdata) wxdata, wxresp['runway_visibility'], _ = core.sanitize_report_list(wxdata) wxdata, wxresp['station'], wxresp['time'] = core.get_station_and_time( wxdata) wxdata, wxresp['clouds'] = core.get_clouds(wxdata) wxdata, wxresp['wind_direction'], wxresp['wind_speed'], \ wxresp['wind_gust'], wxresp['wind_variable_direction'] = core.get_wind(wxdata, units) wxdata, wxresp['altimeter'] = core.get_altimeter(wxdata, units, 'NA') wxdata, wxresp['visibility'] = core.get_visibility(wxdata, units) wxresp['other'], wxresp['temperature'], wxresp[ 'dewpoint'] = core.get_temp_and_dew(wxdata) condition = core.get_flight_rules(wxresp['visibility'], core.get_ceiling(wxresp['clouds'])) wxresp['flight_rules'] = FLIGHT_RULES[condition] wxresp['remarks_info'] = remarks.parse(wxresp['remarks']) wxresp['time'] = core.make_timestamp(wxresp['time']) return MetarData(**wxresp), units
def parse_na(report: str, issued: date = None) -> Tuple[MetarData, Units]: """Parser for the North American METAR variant""" units = Units(**NA_UNITS) resp = {"raw": report} resp["sanitized"], resp["remarks"], data = sanitize(report) data, resp["station"], resp["time"] = core.get_station_and_time(data) data, resp["runway_visibility"] = get_runway_visibility(data) data, resp["clouds"] = core.get_clouds(data) ( data, resp["wind_direction"], resp["wind_speed"], resp["wind_gust"], resp["wind_variable_direction"], ) = core.get_wind(data, units) data, resp["altimeter"] = get_altimeter(data, units, "NA") data, resp["visibility"] = core.get_visibility(data, units) data, resp["temperature"], resp["dewpoint"] = get_temp_and_dew(data) condition = core.get_flight_rules( resp["visibility"], core.get_ceiling(resp["clouds"]) ) resp["other"], resp["wx_codes"] = get_wx_codes(data) resp["flight_rules"] = FLIGHT_RULES[condition] resp["remarks_info"] = remarks.parse(resp["remarks"]) resp["time"] = core.make_timestamp(resp["time"], target_date=issued) return MetarData(**resp), units
def get_visibility(wxdata: [str], units: Units) -> ([str], Number): """ Returns the report list and removed visibility string """ visibility = "" if wxdata: item = copy(wxdata[0]) # Vis reported in statue miles if item.endswith("SM"): # 10SM if item in ("P6SM", "M1/4SM", "M1/8SM"): visibility = item[:-2] elif item[:-2].isdigit(): visibility = str(int(item[:-2])) elif "/" in item: visibility = item[: item.find("SM")] # 1/2SM wxdata.pop(0) units.visibility = "sm" # Vis reported in meters elif len(item) == 4 and item.isdigit(): visibility = wxdata.pop(0) units.visibility = "m" elif ( 7 >= len(item) >= 5 and item[:4].isdigit() and (item[4] in ["M", "N", "S", "E", "W"] or item[4:] == "NDV") ): visibility = wxdata.pop(0)[:4] units.visibility = "m" elif len(item) == 5 and item[1:].isdigit() and item[0] in ["M", "P", "B"]: visibility = wxdata.pop(0)[1:] units.visibility = "m" elif item.endswith("KM") and item[:-2].isdigit(): visibility = item[:-2] + "000" wxdata.pop(0) units.visibility = "m" # Vis statute miles but split Ex: 2 1/2SM elif ( len(wxdata) > 1 and wxdata[1].endswith("SM") and "/" in wxdata[1] and item.isdigit() ): vis1 = wxdata.pop(0) # 2 vis2 = wxdata.pop(0).replace("SM", "") # 1/2 visibility = str(int(vis1) * int(vis2[2]) + int(vis2[0])) + vis2[1:] # 5/2 units.visibility = "sm" return wxdata, make_number(visibility)
def get_altimeter( data: List[str], units: Units, version: str = "NA" ) -> Tuple[List[str], Number]: """Returns the report list and the removed altimeter item Version is 'NA' (North American / default) or 'IN' (International) """ values = [] for _ in range(2): if not data: break value = parse_altimeter(data[-1]) if value is None: break values.append(value) data.pop(-1) if not values: return data, None values.sort(key=lambda x: x.value) altimeter = values[0 if version == "NA" else -1] units.altimeter = "inHg" if altimeter.value < 100 else "hPa" return data, altimeter
def get_visibility(wxdata: [str], units: Units) -> ([str], Number): """ Returns the report list and removed visibility string """ visibility = '' if wxdata: item = copy(wxdata[0]) # Vis reported in statue miles if item.endswith('SM'): # 10SM if item in ('P6SM', 'M1/4SM'): visibility = item[:-2] elif '/' not in item: visibility = str(int(item[:item.find('SM')])) else: visibility = item[:item.find('SM')] # 1/2SM wxdata.pop(0) units.visibility = 'sm' # Vis reported in meters elif len(item) == 4 and item.isdigit(): visibility = wxdata.pop(0) units.visibility = 'm' elif 7 >= len(item) >= 5 and item[:4].isdigit() \ and (item[4] in ['M', 'N', 'S', 'E', 'W'] or item[4:] == 'NDV'): visibility = wxdata.pop(0)[:4] units.visibility = 'm' elif len(item) == 5 and item[1:].isdigit() and item[0] in [ 'M', 'P', 'B' ]: visibility = wxdata.pop(0)[1:] units.visibility = 'm' elif item.endswith('KM') and item[:item.find('KM')].isdigit(): visibility = item[:item.find('KM')] + '000' wxdata.pop(0) units.visibility = 'm' # Vis statute miles but split Ex: 2 1/2SM elif len(wxdata) > 1 and wxdata[1].endswith( 'SM') and '/' in wxdata[1] and item.isdigit(): vis1 = wxdata.pop(0) # 2 vis2 = wxdata.pop(0).replace('SM', '') # 1/2 visibility = str(int(vis1) * int(vis2[2]) + int(vis2[0])) + vis2[1:] # 5/2 units.visibility = 'sm' return wxdata, make_number(visibility)
def get_visibility(wxdata: [str], units: Units) -> ([str], Number): """ Returns the report list and removed visibility string """ visibility = '' if wxdata: item = copy(wxdata[0]) # Vis reported in statue miles if item.endswith('SM'): # 10SM if item in ('P6SM', 'M1/4SM', 'M1/8SM'): visibility = item[:-2] elif '/' not in item: visibility = str(int(item[:item.find('SM')])) else: visibility = item[:item.find('SM')] # 1/2SM wxdata.pop(0) units.visibility = 'sm' # Vis reported in meters elif len(item) == 4 and item.isdigit(): visibility = wxdata.pop(0) units.visibility = 'm' elif 7 >= len(item) >= 5 and item[:4].isdigit() \ and (item[4] in ['M', 'N', 'S', 'E', 'W'] or item[4:] == 'NDV'): visibility = wxdata.pop(0)[:4] units.visibility = 'm' elif len(item) == 5 and item[1:].isdigit() and item[0] in ['M', 'P', 'B']: visibility = wxdata.pop(0)[1:] units.visibility = 'm' elif item.endswith('KM') and item[:item.find('KM')].isdigit(): visibility = item[:item.find('KM')] + '000' wxdata.pop(0) units.visibility = 'm' # Vis statute miles but split Ex: 2 1/2SM elif len(wxdata) > 1 and wxdata[1].endswith('SM') and '/' in wxdata[1] and item.isdigit(): vis1 = wxdata.pop(0) # 2 vis2 = wxdata.pop(0).replace('SM', '') # 1/2 visibility = str(int(vis1) * int(vis2[2]) + int(vis2[0])) + vis2[1:] # 5/2 units.visibility = 'sm' return wxdata, make_number(visibility)
class Reports(AVWXBase): """ Base class containing multiple reports """ raw: Optional[List[str]] = None data: Optional[List[ReportData]] = None units: Units = Units(**NA_UNITS) def __init__(self, icao: str = None, lat: float = None, lon: float = None): if icao: super().__init__(icao) lat = self.station.latitude lon = self.station.longitude elif lat is None or lon is None: raise ValueError("No station or valid coordinates given") self.lat = lat self.lon = lon self.service = NOAA_ADDS("aircraftreport") def __repr__(self) -> str: return f"<avwx.{self.__class__.__name__} lat={self.lat} lon={self.lon}>" @staticmethod def _report_filter(reports: List[str]) -> List[str]: """Applies any report filtering before updating raw_reports""" return reports def _update( self, reports: List[str], issued: Optional[date], disable_post: bool ) -> bool: if not reports: return False reports = self._report_filter(reports) return super()._update(reports, issued, disable_post) def parse(self, reports: Union[str, List[str]], issued: Optional[date] = None): """Updates report data by parsing a given report Can accept a report issue date if not a recent report string """ if isinstance(reports, str): reports = [reports] return self._update(reports, issued, False) def update( self, timeout: int = 10, disable_post: bool = False, ) -> bool: """Updates report data by fetching and parsing the report Returns True if new reports are available, else False """ reports = self.service.fetch(lat=self.lat, lon=self.lon, timeout=timeout) return self._update(reports, None, disable_post) async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: """Async updates report data by fetching and parsing the report""" reports = await self.service.async_fetch( lat=self.lat, lon=self.lon, timeout=timeout ) return self._update(reports, None, disable_post)
from avwx.current.base import Reports from avwx.parsing import core from avwx.static.core import NA_UNITS from avwx.structs import ( Aircraft, Cloud, Icing, Location, Number, PirepData, Timestamp, Turbulence, Units, ) _units = Units(**NA_UNITS) def _root(item: str) -> dict: """ Parses report root data including station and report type """ report_type = None station = None for item in item.split(): if item in ("UA", "UUA"): report_type = item elif not station: station = item return {"station": station, "type": report_type}
def _post_update(self): self.data = parse_mex(self.raw) self.units = Units(**static.UNITS)
class Reports(AVWXBase): """ Base class containing multiple reports """ raw: [str] = None data: [ReportData] = None units: Units = Units(**NA_UNITS) def __init__(self, icao: str = None, lat: float = None, lon: float = None): if icao: super().__init__(icao) lat = self.station.latitude lon = self.station.longitude elif lat is None or lon is None: raise ValueError("No station or valid coordinates given") self.lat = lat self.lon = lon self.service = NOAA_ADDS("aircraftreport") def __repr__(self) -> str: return f"<avwx.{self.__class__.__name__} lat={self.lat} lon={self.lon}>" @staticmethod def _report_filter(reports: [str]) -> [str]: """ Applies any report filtering before updating raw_reports """ return reports def update( self, reports: [str] = None, issued: date = None, timeout: int = 10, disable_post: bool = False, ) -> bool: """ Updates raw and data by fetch recent aircraft reports Can accept a list report strings to parse instead Returns True if new reports are available, else False """ if not reports: reports = self.service.fetch(lat=self.lat, lon=self.lon, timeout=timeout) if not reports: return False issued = None if isinstance(reports, str): reports = [reports] if reports == self.raw: return False self.raw = self._report_filter(reports) self.issued = issued if not disable_post: self._post_update() self._set_meta() return True async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: """ Async version of update """ reports = await self.service.async_fetch(lat=self.lat, lon=self.lon, timeout=timeout) if not reports or reports == self.raw: return False self.raw = reports self.issued = None if not disable_post: self._post_update() self._set_meta() return True
from avwx.current.base import Reports from avwx.parsing import core, sanitization from avwx.static.core import NA_UNITS from avwx.structs import ( Aircraft, Cloud, Icing, Location, Number, PirepData, Timestamp, Turbulence, Units, ) _UNITS = Units(**NA_UNITS) def _root(item: str) -> dict: """ Parses report root data including station and report type """ # pylint: disable=redefined-argument-from-local report_type = None station = None for item in item.split(): if item in ("UA", "UUA"): report_type = item elif not station: station = item return {"station": station, "type": report_type}
from avwx import _core, static, structs from avwx.exceptions import BadStation from avwx.structs import ( Aircraft, Cloud, Icing, Location, Number, PirepData, Timestamp, Turbulance, Units, ) _units = Units(**static.NA_UNITS) def _root(item: str) -> dict: """ Parses report root data including station and report type """ items = item.split() rtype = None station = None # Find valid station for item in items: try: _core.valid_station(item) station = item break