Esempio n. 1
0
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
Esempio n. 2
0
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)
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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)
Esempio n. 7
0
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
Esempio n. 8
0
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)
Esempio n. 9
0
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
Esempio n. 10
0
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
Esempio n. 11
0
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
Esempio n. 12
0
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
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
0
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)
Esempio n. 16
0
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
Esempio n. 17
0
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)
Esempio n. 18
0
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)
Esempio n. 19
0
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)
Esempio n. 20
0
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}
Esempio n. 21
0
 def _post_update(self):
     self.data = parse_mex(self.raw)
     self.units = Units(**static.UNITS)
Esempio n. 22
0
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
Esempio n. 23
0
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}
Esempio n. 24
0
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