avwx
Documentation: https://engine.avwx.rest
Source Code: https://github.com/avwx-rest/avwx-engine
PyPI: https://pypi.org/project/avwx-engine/
AVWX is a global aviation weather fetching and parsing engine. It sources reports from a variety of government sources, parses individual elements, and calculates additional information like flight rules and time range interpolation.
AVWX currently supports:
- Station data and search
- METAR
- TAF
- PIREP
- AIRMET / SIGMET
- NOTAM
- NBM (NBH, NBS, NBE)
- GFS (MAV, MEX)
>>> import avwx
>>> jfk_metar = avwx.Metar('KJFK')
>>> jfk_metar.update()
True
>>> jfk_metar.data.flight_rules
'VFR'
These docs could use some love, and I am not a writer. You can help by making a pull request on GitHub
Installation
AVWX is available on PyPI and requires Python 3.8 and above. Note: the package
name is avwx-engine
, but the import is avwx
python -m pip install avwx-engine
Certain features may require additional libraries which most users won't need. For example, finding stations near a coordinate require scipy which is a large package. Attempting to run these methods without the necessary library will prompt you to install them. If you want to install all dependencies at once, run this instead:
python -m pip install avwx-engine[all]
Tutorial
Let's run through a quick example of fetching and parsing a METAR and TAF. There are other report types, but they follow the same basic API.
>>> import avwx
>>> jfk_metar = avwx.Metar('KJFK')
>>> jfk_metar.update()
True
>>> jfk_metar.raw
'KJFK 281651Z 33021G25KT 10SM FEW060 M08/M23 A3054 RMK AO2 SLP339 T10831228'
>>> jfk_metar.data.flight_rules
'VFR'
>>> jfk_metar.summary
'Winds NNW-330 at 21kt gusting to 25kt, Vis 10sm, Temp -08C, Dew -23C, Alt 30.54inHg, Few clouds at 6000ft'
>>> jfk_metar.station.name
'John F Kennedy International Airport'
Here, we create a METAR object and initialize it to pull data for JFK International airport. The update call fetches the current report, parses it into its individual components, and formats the translations. We then view the original report, the calculated flight rules, and a summary string from the translations. We can also see details of the station if available.
>>> hnl_taf = avwx.Taf('PHNL')
>>> hnl_taf.update()
True
>>> hnl_taf.raw
'PHNL 312058Z 3121/0124 07012G19KT P6SM FEW030 SCT050 FM010500 06007KT P6SM FEW025 SCT045 FM012000 07012G19KT P6SM OVC030 SCT050'
>>> len(hnl_taf.data.forecast)
3
>>> for line in hnl_taf.data.forecast:
... print(f"{line.flight_rules} from {line.start_time.dt.strftime('%d-%H:%M')} to {line.end_time.dt.strftime('%d-%H:%M')}")
...
VFR from 31-21:00 to 01-05:00
VFR from 01-05:00 to 01-20:00
MVFR from 01-20:00 to 01-24:00
Here we start of the same with the Taf object, this time for Honolulu. Because TAFs are forecasts, they contain multiple time periods. Here, we have three: a base and two amendments. Our code shows the different forecasted flight rules for each time period (day-hour). Taf objects have most of the same attributes as Metar objects, so we could also grab the station info if we needed to.
Aviation Reports
- METAR - Current surface conditions
- TAF - Localized 24-hour forecast
- PIREP - Inflight pilot reports
- AIRMET / SIGMET - Weather advisories
- NOTAM - Special events or conditions
Model Forecasts
NBM
- NBH - Current hourly forecast (25 hours)
- NBS - Short-range forecast (6-72 hours)
- NBE - Extended-range forecast (24-192 hours)
GFS
Utilities
- Station - Airport information
- Service - Report fetching services
- Data Updates - Local database updates
- Data Structures - Underlying data structures
- Static Values - Parsing controls and term glossary
- Exceptions - Custom exception handling
1""" 2.. include:: ../docs/launch.md 3""" 4 5from .current.airsigmet import AirSigmet, AirSigManager 6from .current.metar import Metar 7from .current.notam import Notams 8from .current.pirep import Pireps 9from .current.taf import Taf 10from .forecast.gfs import Mav, Mex 11from .forecast.nbm import Nbe, Nbh, Nbs, Nbx 12from .station import Station 13 14 15__all__ = ( 16 "AirSigManager", 17 "AirSigmet", 18 "Mav", 19 "Metar", 20 "Mex", 21 "Nbe", 22 "Nbh", 23 "Nbs", 24 "Nbx", 25 "Notams", 26 "Pireps", 27 "Station", 28 "Taf", 29)
120class AirSigManager: 121 """ 122 Because of the global nature of these report types, we don't initialize a 123 report class with a station ident like the other report types. Instead, we 124 use a class to manage and update the list of all active SIGMET and AIRMET 125 reports. 126 127 ```python 128 >>> from avwx import AirSigManager 129 >>> from avwx.structs import Coord 130 >>> manager = AirSigManager() 131 >>> manager.update() 132 True 133 >>> manager.last_updated 134 datetime.datetime(2022, 3, 27, 5, 54, 21, 516741, tzinfo=datetime.timezone.utc) 135 >>> len(manager.reports) 136 113 137 >>> len(manager.contains(Coord(lat=33.12, lon=-105))) 138 5 139 >>> manager.reports[0].data.bulletin.type 140 Code(repr='WA', value='airmet') 141 >>> manager.reports[0].data.type 142 'AIRMET SIERRA FOR IFR AND MTN OBSCN' 143 ``` 144 """ 145 146 _services: List[Service] 147 _raw: List[Tuple[str, Optional[str]]] 148 last_updated: Optional[datetime] = None 149 raw: List[str] 150 reports: Optional[List[AirSigmet]] = None 151 152 def __init__(self): # type: ignore 153 self._services = [NOAA_Bulk("airsigmet"), NOAA_Intl("airsigmet")] 154 self._raw, self.raw = [], [] 155 156 async def _update( 157 self, index: int, timeout: int 158 ) -> List[Tuple[str, Optional[str]]]: 159 source = self._services[index].root 160 reports = await self._services[index].async_fetch(timeout=timeout) # type: ignore 161 raw: List[Tuple[str, Optional[str]]] = [ 162 (report, source) for report in reports if report 163 ] 164 return raw 165 166 def update(self, timeout: int = 10, disable_post: bool = False) -> bool: 167 """Updates fetched reports and returns whether they've changed""" 168 return aio.run(self.async_update(timeout, disable_post)) 169 170 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 171 """Updates fetched reports and returns whether they've changed""" 172 coros = [self._update(i, timeout) for i in range(len(self._services))] 173 data = await aio.gather(*coros) 174 raw = list(chain.from_iterable(data)) 175 reports = [i[0] for i in raw] 176 if raw == self._raw: 177 return False 178 self._raw, self.raw = raw, reports 179 self.last_updated = datetime.now(tz=timezone.utc) 180 # Parse reports if not disabled 181 if not disable_post: 182 parsed = [] 183 for report, source in raw: 184 try: 185 if obj := AirSigmet.from_report(report): 186 obj.source = source 187 parsed.append(obj) 188 except Exception as exc: # pylint: disable=broad-except 189 exceptions.exception_intercept(exc, raw={"report": report}) 190 self.reports = parsed 191 return True 192 193 def along(self, coords: List[Coord]) -> List[AirSigmet]: 194 """Returns available reports the intersect a flight path""" 195 if LineString is None: 196 raise ModuleNotFoundError("Install avwx-engine[shape] to use this feature") 197 if self.reports is None: 198 return [] 199 path = LineString([c.pair for c in coords]) 200 return [r for r in self.reports if r.intersects(path)] 201 202 def contains(self, coord: Coord) -> List[AirSigmet]: 203 """Returns available reports that contain a coordinate""" 204 if self.reports is None: 205 return [] 206 return [r for r in self.reports if r.contains(coord)]
Because of the global nature of these report types, we don't initialize a report class with a station ident like the other report types. Instead, we use a class to manage and update the list of all active SIGMET and AIRMET reports.
>>> from avwx import AirSigManager
>>> from avwx.structs import Coord
>>> manager = AirSigManager()
>>> manager.update()
True
>>> manager.last_updated
datetime.datetime(2022, 3, 27, 5, 54, 21, 516741, tzinfo=datetime.timezone.utc)
>>> len(manager.reports)
113
>>> len(manager.contains(Coord(lat=33.12, lon=-105)))
5
>>> manager.reports[0].data.bulletin.type
Code(repr='WA', value='airmet')
>>> manager.reports[0].data.type
'AIRMET SIERRA FOR IFR AND MTN OBSCN'
166 def update(self, timeout: int = 10, disable_post: bool = False) -> bool: 167 """Updates fetched reports and returns whether they've changed""" 168 return aio.run(self.async_update(timeout, disable_post))
Updates fetched reports and returns whether they've changed
170 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 171 """Updates fetched reports and returns whether they've changed""" 172 coros = [self._update(i, timeout) for i in range(len(self._services))] 173 data = await aio.gather(*coros) 174 raw = list(chain.from_iterable(data)) 175 reports = [i[0] for i in raw] 176 if raw == self._raw: 177 return False 178 self._raw, self.raw = raw, reports 179 self.last_updated = datetime.now(tz=timezone.utc) 180 # Parse reports if not disabled 181 if not disable_post: 182 parsed = [] 183 for report, source in raw: 184 try: 185 if obj := AirSigmet.from_report(report): 186 obj.source = source 187 parsed.append(obj) 188 except Exception as exc: # pylint: disable=broad-except 189 exceptions.exception_intercept(exc, raw={"report": report}) 190 self.reports = parsed 191 return True
Updates fetched reports and returns whether they've changed
193 def along(self, coords: List[Coord]) -> List[AirSigmet]: 194 """Returns available reports the intersect a flight path""" 195 if LineString is None: 196 raise ModuleNotFoundError("Install avwx-engine[shape] to use this feature") 197 if self.reports is None: 198 return [] 199 path = LineString([c.pair for c in coords]) 200 return [r for r in self.reports if r.intersects(path)]
Returns available reports the intersect a flight path
59class AirSigmet(AVWXBase): 60 """ 61 In addition to the manager, you can use the `avwx.AirSigmet` class like any 62 other report when you supply the report string via `parse` or 63 `from_report`. 64 65 ```python 66 >>> from avwx import AirSigmet 67 >>> report = 'WSPR31 SPJC 270529 SPIM SIGMET 3 VALID 270530/270830 SPJC- SPIM LIMA FIR EMBD TS OBS AT 0510Z NE OF LINE S0406 W07103 - S0358 W07225 - S0235 W07432 - S0114 W07503 TOP FL410 MOV SW NC=' 68 >>> sigmet = AirSigmet.from_report(report) 69 True 70 >>> sigmet.last_updated 71 datetime.datetime(2022, 3, 27, 6, 29, 33, 300935, tzinfo=datetime.timezone.utc) 72 >>> sigmet.data.observation.coords 73 [Coord(lat=-4.06, lon=-71.03, repr='S0406 W07103'), 74 Coord(lat=-3.58, lon=-72.25, repr='S0358 W07225'), 75 Coord(lat=-2.35, lon=-74.32, repr='S0235 W07432'), 76 Coord(lat=-1.14, lon=-75.03, repr='S0114 W07503')] 77 >>> sigmet.data.observation.intensity 78 Code(repr='NC', value='No change') 79 >>> sigmet.data.observation.ceiling 80 Number(repr='FL410', value=410, spoken='flight level four one zero') 81 ``` 82 """ 83 84 data: Optional[AirSigmetData] = None 85 86 def _post_parse(self) -> None: 87 if self.raw: 88 self.data, self.units = parse(self.raw, self.issued) 89 90 @staticmethod 91 def sanitize(report: str) -> str: 92 """Sanitizes the report string""" 93 return sanitize(report) 94 95 def intersects(self, path: LineString) -> bool: 96 """Returns True if the report area intersects a flight path""" 97 if LineString is None: 98 raise ModuleNotFoundError("Install avwx-engine[shape] to use this feature") 99 if not self.data: 100 return False 101 for data in (self.data.observation, self.data.forecast): 102 if data: 103 poly = data.poly 104 if poly and path.intersects(poly): 105 return True 106 return False 107 108 def contains(self, coord: Coord) -> bool: 109 """Returns True if the report area contains a coordinate""" 110 if not self.data: 111 return False 112 for data in (self.data.observation, self.data.forecast): 113 if data: 114 poly = data.poly 115 if poly and coord.point.within(poly): 116 return True 117 return False
In addition to the manager, you can use the avwx.AirSigmet
class like any
other report when you supply the report string via parse
or
from_report
.
>>> from avwx import AirSigmet
>>> report = 'WSPR31 SPJC 270529 SPIM SIGMET 3 VALID 270530/270830 SPJC- SPIM LIMA FIR EMBD TS OBS AT 0510Z NE OF LINE S0406 W07103 - S0358 W07225 - S0235 W07432 - S0114 W07503 TOP FL410 MOV SW NC='
>>> sigmet = AirSigmet.from_report(report)
True
>>> sigmet.last_updated
datetime.datetime(2022, 3, 27, 6, 29, 33, 300935, tzinfo=datetime.timezone.utc)
>>> sigmet.data.observation.coords
[Coord(lat=-4.06, lon=-71.03, repr='S0406 W07103'),
Coord(lat=-3.58, lon=-72.25, repr='S0358 W07225'),
Coord(lat=-2.35, lon=-74.32, repr='S0235 W07432'),
Coord(lat=-1.14, lon=-75.03, repr='S0114 W07503')]
>>> sigmet.data.observation.intensity
Code(repr='NC', value='No change')
>>> sigmet.data.observation.ceiling
Number(repr='FL410', value=410, spoken='flight level four one zero')
90 @staticmethod 91 def sanitize(report: str) -> str: 92 """Sanitizes the report string""" 93 return sanitize(report)
Sanitizes the report string
95 def intersects(self, path: LineString) -> bool: 96 """Returns True if the report area intersects a flight path""" 97 if LineString is None: 98 raise ModuleNotFoundError("Install avwx-engine[shape] to use this feature") 99 if not self.data: 100 return False 101 for data in (self.data.observation, self.data.forecast): 102 if data: 103 poly = data.poly 104 if poly and path.intersects(poly): 105 return True 106 return False
Returns True if the report area intersects a flight path
108 def contains(self, coord: Coord) -> bool: 109 """Returns True if the report area contains a coordinate""" 110 if not self.data: 111 return False 112 for data in (self.data.observation, self.data.forecast): 113 if data: 114 poly = data.poly 115 if poly and coord.point.within(poly): 116 return True 117 return False
Returns True if the report area contains a coordinate
Inherited Members
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- units
- from_report
- parse
49class Mav(Forecast): 50 ''' 51 The Mav class offers an object-oriented approach to managing MOS MAV data 52 for a single station. 53 54 Below is typical usage for fetching and pulling MAV data for KJFK. 55 56 ```python 57 >>> from avwx import Mav 58 >>> kjfk = Mav("KJFK") 59 >>> kjfk.station.name 60 'John F Kennedy International Airport' 61 >>> kjfk.update() 62 True 63 >>> kjfk.last_updated 64 datetime.datetime(2020, 4, 20, 1, 7, 7, 393270, tzinfo=datetime.timezone.utc) 65 >>> print(kjfk.raw) 66 """ 67 KJFK GFS MOS GUIDANCE 4/19/2020 1800 UTC 68 DT /APR 20 /APR 21 /APR 22 69 HR 00 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21 00 03 06 12 18 70 N/X 46 58 44 58 37 71 TMP 53 52 50 48 48 50 54 56 51 49 47 46 49 53 55 52 47 45 43 41 54 72 DPT 43 41 37 35 33 30 28 27 28 30 32 34 37 39 37 32 26 23 22 18 14 73 CLD OV OV OV OV OV OV OV SC FW CL CL FW BK OV OV OV BK FW CL FW SC 74 WDR 20 22 26 35 02 03 02 02 34 19 20 18 18 18 18 23 29 30 29 29 28 75 WSP 20 13 07 08 11 14 14 11 05 03 04 06 11 19 25 21 22 25 20 19 22 76 P06 0 12 9 1 0 1 29 68 8 2 0 77 P12 12 9 2 69 15 78 Q06 0 0 0 0 0 0 0 2 0 0 0 79 Q12 0 0 0 2 0 80 T06 0/ 4 1/ 0 1/ 0 0/ 0 0/ 0 0/ 0 5/ 3 13/13 0/ 0 0/ 8 81 T12 1/ 2 0/ 0 9/ 6 14/13 1/ 8 82 POZ 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 83 POS 0 0 0 0 0 2 0 6 6 9 9 0 16 8 0 4 4 47 60 67 42 84 TYP R R R R R R R R R R R R R R R R R R S S R 85 SNW 0 0 86 CIG 7 7 7 7 6 6 6 8 8 8 8 8 8 6 6 6 7 8 8 8 8 87 VIS 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 7 7 7 7 7 88 OBV N N N N N N N N N N N N N N N N N N N N N 89 """ 90 >>> len(kjfk.data.forecast) 91 21 92 >>> kjfk.data.forecast[0].ceiling 93 Code(repr='7', value='6600 - 12,000 feet') 94 ``` 95 96 The `parse` and `from_report` methods can parse a report string if you want 97 to override the normal fetching process. 98 ''' 99 100 report_type = "mav" 101 _service_class = NOAA_GFS # type: ignore 102 103 async def _post_update(self) -> None: 104 if self.raw is None: 105 return 106 self.data = parse_mav(self.raw) 107 self.units = Units(**static.UNITS) 108 109 def _post_parse(self) -> None: 110 if self.raw is None: 111 return 112 self.data = parse_mav(self.raw) 113 self.units = Units(**static.UNITS)
The Mav class offers an object-oriented approach to managing MOS MAV data for a single station.
Below is typical usage for fetching and pulling MAV data for KJFK.
>>> from avwx import Mav
>>> kjfk = Mav("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2020, 4, 20, 1, 7, 7, 393270, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
KJFK GFS MOS GUIDANCE 4/19/2020 1800 UTC
DT /APR 20 /APR 21 /APR 22
HR 00 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21 00 03 06 12 18
N/X 46 58 44 58 37
TMP 53 52 50 48 48 50 54 56 51 49 47 46 49 53 55 52 47 45 43 41 54
DPT 43 41 37 35 33 30 28 27 28 30 32 34 37 39 37 32 26 23 22 18 14
CLD OV OV OV OV OV OV OV SC FW CL CL FW BK OV OV OV BK FW CL FW SC
WDR 20 22 26 35 02 03 02 02 34 19 20 18 18 18 18 23 29 30 29 29 28
WSP 20 13 07 08 11 14 14 11 05 03 04 06 11 19 25 21 22 25 20 19 22
P06 0 12 9 1 0 1 29 68 8 2 0
P12 12 9 2 69 15
Q06 0 0 0 0 0 0 0 2 0 0 0
Q12 0 0 0 2 0
T06 0/ 4 1/ 0 1/ 0 0/ 0 0/ 0 0/ 0 5/ 3 13/13 0/ 0 0/ 8
T12 1/ 2 0/ 0 9/ 6 14/13 1/ 8
POZ 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
POS 0 0 0 0 0 2 0 6 6 9 9 0 16 8 0 4 4 47 60 67 42
TYP R R R R R R R R R R R R R R R R R R S S R
SNW 0 0
CIG 7 7 7 7 6 6 6 8 8 8 8 8 8 6 6 6 7 8 8 8 8
VIS 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 7 7 7 7 7
OBV N N N N N N N N N N N N N N N N N N N N N
"""
>>> len(kjfk.data.forecast)
21
>>> kjfk.data.forecast[0].ceiling
Code(repr='7', value='6600 - 12,000 feet')
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- units
- parse
- sanitize
38class Metar(Report): 39 """ 40 The Metar class offers an object-oriented approach to managing METAR data 41 for a single station. 42 43 Below is typical usage for fetching and pulling METAR data for KJFK. 44 45 ```python 46 >>> from avwx import Metar 47 >>> kjfk = Metar("KJFK") 48 >>> kjfk.station.name 49 'John F Kennedy International Airport' 50 >>> kjfk.update() 51 True 52 >>> kjfk.last_updated 53 datetime.datetime(2018, 3, 4, 23, 36, 6, 62376) 54 >>> kjfk.raw 55 'KJFK 042251Z 32023G32KT 10SM BKN060 04/M08 A3008 RMK AO2 PK WND 32032/2251 SLP184 T00441078' 56 >>> kjfk.data.flight_rules 57 'VFR' 58 >>> kjfk.translations.remarks 59 {'AO2': 'Automated with precipitation sensor', 'SLP184': 'Sea level pressure: 1018.4 hPa', 'T00441078': 'Temperature 4.4°C and dewpoint -7.8°C'} 60 ``` 61 62 The `parse` and `from_report` methods can parse a report string if you want 63 to override the normal fetching process. Here's an example of a really bad 64 day. 65 66 ```python 67 >>> from avwx import Metar 68 >>> report = 'KSFO 031254Z 36024G55KT 320V040 1/8SM R06/0200D +TS VCFC OVC050 BKN040TCU 14/10 A2978 RMK AIRPORT CLOSED' 69 >>> ksfo = Metar.from_report(report) 70 True 71 >>> ksfo.station.city 72 'San Francisco' 73 >>> ksfo.last_updated 74 datetime.datetime(2018, 3, 4, 23, 54, 4, 353757, tzinfo=datetime.timezone.utc) 75 >>> ksfo.data.flight_rules 76 'LIFR' 77 >>> ksfo.translations.clouds 78 'Broken layer at 4000ft (Towering Cumulus), Overcast layer at 5000ft - Reported AGL' 79 >>> ksfo.summary 80 'Winds N-360 (variable 320 to 040) at 24kt gusting to 55kt, Vis 0.125sm, Temp 14C, Dew 10C, Alt 29.78inHg, Heavy Thunderstorm, Vicinity Funnel Cloud, Broken layer at 4000ft (Towering Cumulus), Overcast layer at 5000ft' 81 ``` 82 """ 83 84 data: Optional[MetarData] = None 85 translations: Optional[MetarTrans] = None 86 87 async def _pull_from_default(self) -> None: 88 """Checks for a more recent report from NOAA. Only sync""" 89 service = NOAA(self.__class__.__name__.lower()) 90 if self.code is None: 91 return 92 report = await service.async_fetch(self.code) 93 if report is not None: 94 data, units, sans = parse(self.code, report, self.issued) 95 if not data or data.time is None or data.time.dt is None: 96 return 97 if ( 98 not self.data 99 or self.data.time is None 100 or self.data.time.dt is None 101 or data.time.dt > self.data.time.dt 102 ): 103 self.data, self.units, self.sanitization = data, units, sans 104 self.source = service.root 105 106 @property 107 def _should_check_default(self) -> bool: 108 """Returns True if pulled from regional source and potentially out of date""" 109 if isinstance(self.service, NOAA) or self.source is None: 110 return False 111 112 if self.data is None or self.data.time is None or self.data.time.dt is None: 113 return True 114 time_since = datetime.now(tz=timezone.utc) - self.data.time.dt 115 return time_since > timedelta(minutes=90) 116 117 def _calculate_altitudes(self) -> None: 118 """Adds the pressure and density altitudes to data if all fields are available""" 119 if self.data is None or self.station is None or self.units is None: 120 return 121 # Select decimal temperature if available 122 temp = self.data.temperature 123 if self.data.remarks_info is not None: 124 temp = self.data.remarks_info.temperature_decimal or temp 125 alt = self.data.altimeter 126 if temp is None or temp.value is None or alt is None or alt.value is None: 127 return 128 elev = self.station.elevation_ft 129 if elev is None: 130 return 131 self.data.pressure_altitude = core.pressure_altitude( 132 alt.value, elev, self.units.altimeter 133 ) 134 self.data.density_altitude = core.density_altitude( 135 alt.value, temp.value, elev, self.units 136 ) 137 138 async def _post_update(self) -> None: 139 if self.code is None or self.raw is None: 140 return 141 self.data, self.units, self.sanitization = parse( 142 self.code, self.raw, self.issued 143 ) 144 if self._should_check_default: 145 await self._pull_from_default() 146 if self.data is None or self.units is None: 147 return 148 self._calculate_altitudes() 149 self.translations = translate_metar(self.data, self.units) 150 151 def _post_parse(self) -> None: 152 if self.code is None or self.raw is None: 153 return 154 self.data, self.units, self.sanitization = parse( 155 self.code, self.raw, self.issued 156 ) 157 if self.data is None or self.units is None: 158 return 159 self._calculate_altitudes() 160 self.translations = translate_metar(self.data, self.units) 161 162 @staticmethod 163 def sanitize(report: str) -> str: 164 """Sanitizes a METAR string""" 165 return sanitize(report)[0] 166 167 @property 168 def summary(self) -> Optional[str]: 169 """Condensed report summary created from translations""" 170 if not self.translations: 171 self.update() 172 return None if self.translations is None else summary.metar(self.translations) 173 174 @property 175 def speech(self) -> Optional[str]: 176 """Report summary designed to be read by a text-to-speech program""" 177 if not self.data: 178 self.update() 179 if self.data is None or self.units is None: 180 return None 181 return speech.metar(self.data, self.units)
The Metar class offers an object-oriented approach to managing METAR data for a single station.
Below is typical usage for fetching and pulling METAR data for KJFK.
>>> from avwx import Metar
>>> kjfk = Metar("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2018, 3, 4, 23, 36, 6, 62376)
>>> kjfk.raw
'KJFK 042251Z 32023G32KT 10SM BKN060 04/M08 A3008 RMK AO2 PK WND 32032/2251 SLP184 T00441078'
>>> kjfk.data.flight_rules
'VFR'
>>> kjfk.translations.remarks
{'AO2': 'Automated with precipitation sensor', 'SLP184': 'Sea level pressure: 1018.4 hPa', 'T00441078': 'Temperature 4.4°C and dewpoint -7.8°C'}
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process. Here's an example of a really bad
day.
>>> from avwx import Metar
>>> report = 'KSFO 031254Z 36024G55KT 320V040 1/8SM R06/0200D +TS VCFC OVC050 BKN040TCU 14/10 A2978 RMK AIRPORT CLOSED'
>>> ksfo = Metar.from_report(report)
True
>>> ksfo.station.city
'San Francisco'
>>> ksfo.last_updated
datetime.datetime(2018, 3, 4, 23, 54, 4, 353757, tzinfo=datetime.timezone.utc)
>>> ksfo.data.flight_rules
'LIFR'
>>> ksfo.translations.clouds
'Broken layer at 4000ft (Towering Cumulus), Overcast layer at 5000ft - Reported AGL'
>>> ksfo.summary
'Winds N-360 (variable 320 to 040) at 24kt gusting to 55kt, Vis 0.125sm, Temp 14C, Dew 10C, Alt 29.78inHg, Heavy Thunderstorm, Vicinity Funnel Cloud, Broken layer at 4000ft (Towering Cumulus), Overcast layer at 5000ft'
162 @staticmethod 163 def sanitize(report: str) -> str: 164 """Sanitizes a METAR string""" 165 return sanitize(report)[0]
Sanitizes a METAR string
167 @property 168 def summary(self) -> Optional[str]: 169 """Condensed report summary created from translations""" 170 if not self.translations: 171 self.update() 172 return None if self.translations is None else summary.metar(self.translations)
Condensed report summary created from translations
174 @property 175 def speech(self) -> Optional[str]: 176 """Report summary designed to be read by a text-to-speech program""" 177 if not self.data: 178 self.update() 179 if self.data is None or self.units is None: 180 return None 181 return speech.metar(self.data, self.units)
Report summary designed to be read by a text-to-speech program
Inherited Members
- avwx.current.base.Report
- Report
- sanitization
- avwx.base.ManagedReport
- code
- station
- service
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- units
- parse
116class Mex(Forecast): 117 ''' 118 The Mex class offers an object-oriented approach to managing MOS MEX data 119 for a single station. 120 121 Below is typical usage for fetching and pulling MAV data for KJFK. 122 123 ```python 124 >>> from avwx import Mex 125 >>> kjfk = Mex("KJFK") 126 >>> kjfk.station.name 127 'John F Kennedy International Airport' 128 >>> kjfk.update() 129 True 130 >>> kjfk.last_updated 131 datetime.datetime(2020, 4, 20, 12, 7, 7, 393270, tzinfo=datetime.timezone.utc) 132 >>> print(kjfk.raw) 133 """ 134 KJFK GFSX MOS GUIDANCE 4/20/2020 0000 UTC 135 FHR 24| 36 48| 60 72| 84 96|108 120|132 144|156 168|180 192 136 MON 20| TUE 21| WED 22| THU 23| FRI 24| SAT 25| SUN 26| MON 27 CLIMO 137 X/N 57| 45 59| 37 56| 40 52| 49 58| 46 59| 48 59| 44 58 45 63 138 TMP 50| 49 48| 41 49| 45 48| 52 51| 50 53| 51 52| 48 51 139 DPT 31| 39 26| 17 17| 24 40| 46 43| 40 44| 43 40| 35 31 140 CLD OV| OV OV| CL CL| OV OV| OV OV| PC OV| OV OV| OV OV 141 WND 13| 14 26| 26 21| 16 13| 18 15| 16 12| 15 19| 19 11 142 P12 9| 1 73| 7 0| 9 43| 73 63| 27 51| 64 37| 35 32 24 23 143 P24 | 73| 7| 43| 77| 61| 73| 44 36 144 Q12 0| 0 2| 0 0| 0 1| 5 3| 0 2| 5 | 145 Q24 | 1| 0| 0| 5| 2| | 146 T12 1| 0 12| 1 0| 4 4| 8 11| 3 3| 14 7| 5 9 147 T24 | 1 | 14 | 4 | 12 | 11 | 14 | 11 148 PZP 0| 0 1| 0 2| 4 1| 0 0| 0 0| 0 0| 0 0 149 PSN 0| 0 0| 37 25| 15 4| 0 0| 0 0| 2 0| 3 5 150 PRS 0| 2 1| 32 28| 19 4| 0 1| 1 1| 1 1| 8 9 151 TYP R| R R| RS RS| R R| R R| R R| R R| R R 152 SNW | 0| 0| 0| 0| 0| | 153 """ 154 >>> len(kjfk.data.forecast) 155 15 156 >>> kjfk.data.forecast[2].precip_chance_24 157 Number(repr='73', value=73, spoken='seven three') 158 ``` 159 160 The `parse` and `from_report` methods can parse a report string if you want 161 to override the normal fetching process. 162 ''' 163 164 report_type = "mex" 165 _service_class = NOAA_GFS # type: ignore 166 167 async def _post_update(self) -> None: 168 if self.raw is None: 169 return 170 self.data = parse_mex(self.raw) 171 self.units = Units(**static.UNITS) 172 173 def _post_parse(self) -> None: 174 if self.raw is None: 175 return 176 self.data = parse_mex(self.raw) 177 self.units = Units(**static.UNITS)
The Mex class offers an object-oriented approach to managing MOS MEX data for a single station.
Below is typical usage for fetching and pulling MAV data for KJFK.
>>> from avwx import Mex
>>> kjfk = Mex("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2020, 4, 20, 12, 7, 7, 393270, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
KJFK GFSX MOS GUIDANCE 4/20/2020 0000 UTC
FHR 24| 36 48| 60 72| 84 96|108 120|132 144|156 168|180 192
MON 20| TUE 21| WED 22| THU 23| FRI 24| SAT 25| SUN 26| MON 27 CLIMO
X/N 57| 45 59| 37 56| 40 52| 49 58| 46 59| 48 59| 44 58 45 63
TMP 50| 49 48| 41 49| 45 48| 52 51| 50 53| 51 52| 48 51
DPT 31| 39 26| 17 17| 24 40| 46 43| 40 44| 43 40| 35 31
CLD OV| OV OV| CL CL| OV OV| OV OV| PC OV| OV OV| OV OV
WND 13| 14 26| 26 21| 16 13| 18 15| 16 12| 15 19| 19 11
P12 9| 1 73| 7 0| 9 43| 73 63| 27 51| 64 37| 35 32 24 23
P24 | 73| 7| 43| 77| 61| 73| 44 36
Q12 0| 0 2| 0 0| 0 1| 5 3| 0 2| 5 |
Q24 | 1| 0| 0| 5| 2| |
T12 1| 0 12| 1 0| 4 4| 8 11| 3 3| 14 7| 5 9
T24 | 1 | 14 | 4 | 12 | 11 | 14 | 11
PZP 0| 0 1| 0 2| 4 1| 0 0| 0 0| 0 0| 0 0
PSN 0| 0 0| 37 25| 15 4| 0 0| 0 0| 2 0| 3 5
PRS 0| 2 1| 32 28| 19 4| 0 1| 1 1| 1 1| 8 9
TYP R| R R| RS RS| R R| R R| R R| R R| R R
SNW | 0| 0| 0| 0| 0| |
"""
>>> len(kjfk.data.forecast)
15
>>> kjfk.data.forecast[2].precip_chance_24
Number(repr='73', value=73, spoken='seven three')
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- units
- parse
- sanitize
351class Nbe(_Nbm): 352 ''' 353 Class to handle NBM NBE report data 354 355 Below is typical usage for fetching and pulling NBE data for KJFK. 356 357 ```python 358 >>> from avwx import Nbe 359 >>> kjfk = Nbe("KJFK") 360 >>> kjfk.station.name 361 'John F Kennedy International Airport' 362 >>> kjfk.update() 363 True 364 >>> kjfk.last_updated 365 datetime.datetime(2020, 7, 28, 1, 23, 4, 909939, tzinfo=datetime.timezone.utc) 366 >>> print(kjfk.raw) 367 """ 368 KJFK NBM V3.2 NBE GUIDANCE 7/28/2020 0000 UTC 369 WED 29| THU 30| FRI 31| SAT 01| SUN 02| MON 03| TUE 04|WED CLIMO 370 UTC 00 12| 00 12| 00 12| 00 12| 00 12| 00 12| 00 12| 00 371 FHR 24 36| 48 60| 72 84| 96 108|120 132|144 156|168 180|192 372 X/N 93 76| 91 76| 90 74| 86 72| 87 73| 85 74| 86 72| 84 68 83 373 TMP 84 80| 83 80| 81 78| 78 76| 78 78| 78 78| 78 76| 76 374 DPT 72 69| 68 69| 71 68| 67 66| 68 69| 70 71| 70 68| 69 375 SKY 61 21| 23 47| 80 73| 47 31| 30 54| 68 65| 66 59| 32 376 WDR 25 35| 20 26| 20 2| 16 1| 16 7| 16 24| 22 34| 18 377 WSP 5 2| 6 3| 5 4| 3 5| 7 4| 6 4| 5 4| 4 378 GST 11 4| 13 6| 13 10| 9 10| 13 7| 13 9| 16 9| 12 379 P12 48 23| 8 1| 23 28| 28 16| 18 17| 30 41| 46 31| 32 19 18 380 Q12 10 0| 0 0| 0 0| 0 0| 0 0| 0 64| 77 81| 83 381 Q24 | 0 | 0 | 0 | 0 | 0 |141 |164 382 DUR 2 1| 0 0| 0 0| 0 0| 0 0| 2 12| 12 12| 12 383 T12 46 32| 6 8| 21 22| 17 5| 6 5| 25 23| 19 18| 18 384 PZR 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 385 PSN 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 386 PPL 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 387 PRA 100 100|100 100|100 100|100 100|100 100|100 100|100 100|100 388 S12 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 389 SLV 138 114|111 119|119 121|113 101|108 117|134 132|124 123|121 390 I12 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 391 S24 | 0 | 0 | 0 | 0 | 0 | 0 | 0 392 SOL 100 320|190 230|270 250|360 290|370 30|190 260|250 230|450 393 """ 394 >>> len(kjfk.data.forecast) 395 25 396 >>> kjfk.data.forecast[0].wind_direction 397 Number(repr='25', value=250, spoken='two five zero') 398 >>> print(kjfk.data.forecast[1].precip_duration.value, kjfk.units.duration) 399 1 hour 400 ``` 401 402 The `parse` and `from_report` methods can parse a report string if you want 403 to override the normal fetching process. 404 ''' 405 406 report_type = "nbe" 407 _parser = staticmethod(parse_nbe)
Class to handle NBM NBE report data
Below is typical usage for fetching and pulling NBE data for KJFK.
>>> from avwx import Nbe
>>> kjfk = Nbe("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2020, 7, 28, 1, 23, 4, 909939, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
KJFK NBM V3.2 NBE GUIDANCE 7/28/2020 0000 UTC
WED 29| THU 30| FRI 31| SAT 01| SUN 02| MON 03| TUE 04|WED CLIMO
UTC 00 12| 00 12| 00 12| 00 12| 00 12| 00 12| 00 12| 00
FHR 24 36| 48 60| 72 84| 96 108|120 132|144 156|168 180|192
X/N 93 76| 91 76| 90 74| 86 72| 87 73| 85 74| 86 72| 84 68 83
TMP 84 80| 83 80| 81 78| 78 76| 78 78| 78 78| 78 76| 76
DPT 72 69| 68 69| 71 68| 67 66| 68 69| 70 71| 70 68| 69
SKY 61 21| 23 47| 80 73| 47 31| 30 54| 68 65| 66 59| 32
WDR 25 35| 20 26| 20 2| 16 1| 16 7| 16 24| 22 34| 18
WSP 5 2| 6 3| 5 4| 3 5| 7 4| 6 4| 5 4| 4
GST 11 4| 13 6| 13 10| 9 10| 13 7| 13 9| 16 9| 12
P12 48 23| 8 1| 23 28| 28 16| 18 17| 30 41| 46 31| 32 19 18
Q12 10 0| 0 0| 0 0| 0 0| 0 0| 0 64| 77 81| 83
Q24 | 0 | 0 | 0 | 0 | 0 |141 |164
DUR 2 1| 0 0| 0 0| 0 0| 0 0| 2 12| 12 12| 12
T12 46 32| 6 8| 21 22| 17 5| 6 5| 25 23| 19 18| 18
PZR 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0
PSN 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0
PPL 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0
PRA 100 100|100 100|100 100|100 100|100 100|100 100|100 100|100
S12 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0
SLV 138 114|111 119|119 121|113 101|108 117|134 132|124 123|121
I12 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0 0| 0
S24 | 0 | 0 | 0 | 0 | 0 | 0 | 0
SOL 100 320|190 230|270 250|360 290|370 30|190 260|250 230|450
"""
>>> len(kjfk.data.forecast)
25
>>> kjfk.data.forecast[0].wind_direction
Number(repr='25', value=250, spoken='two five zero')
>>> print(kjfk.data.forecast[1].precip_duration.value, kjfk.units.duration)
1 hour
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.forecast.nbm._Nbm
- units
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- parse
- sanitize
222class Nbh(_Nbm): 223 ''' 224 Class to handle NBM NBH report data 225 226 Below is typical usage for fetching and pulling NBH data for KJFK. 227 228 ```python 229 >>> from avwx import Nbh 230 >>> kjfk = Nbh("KJFK") 231 >>> kjfk.station.name 232 'John F Kennedy International Airport' 233 >>> kjfk.update() 234 True 235 >>> kjfk.last_updated 236 datetime.datetime(2020, 7, 26, 20, 37, 42, 352220, tzinfo=datetime.timezone.utc) 237 >>> print(kjfk.raw) 238 """ 239 KJFK NBM V3.2 NBH GUIDANCE 7/26/2020 1900 UTC 240 UTC 20 21 22 23 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 241 TMP 90 89 88 87 85 84 82 81 81 80 79 78 77 76 76 78 81 84 87 89 90 91 92 92 93 242 DPT 69 68 66 66 65 65 65 66 66 66 66 67 67 66 67 67 67 67 67 68 67 67 67 67 67 243 SKY 9 14 41 58 61 71 66 55 39 37 39 43 40 38 29 21 19 26 24 27 22 14 22 26 26 244 WDR 22 22 23 24 25 25 25 25 25 25 25 26 26 26 26 26 26 26 25 24 24 23 23 22 23 245 WSP 10 9 9 8 7 6 5 5 5 6 5 5 5 5 4 4 5 5 7 8 8 9 10 10 10 246 GST 17 16 16 15 14 12 11 12 12 12 12 11 11 9 9 9 9 10 11 13 14 15 16 17 17 247 P01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 248 P06 0 0 0 249 Q01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 250 DUR 0 251 T01 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 252 PZR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 253 PSN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 254 PPL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 PRA 100100100100100100100100100100100100100100100100100100100100100100100100100 256 S01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 257 SLV 99101102103105105105106105104106104104104104103102100 99 98 98 99100101102 258 I01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 259 CIG 888888888360360350340888888888888888888888888888888888888888888888888888888 260 VIS 110120120140140130120120120120110110110110110120130140150150150150150150150 261 LCB 80 60999999999999999999999999999999999999999999999999999999999999999999999 262 MHT 26 26 20 19 8 7 6 4 4 5 5 5 6 4 5 6 6 11 20 27 37 39 43 39 31 263 TWD 24 26 26 27 26 26 24 23 24 26 26 26 27 27 28 27 27 27 27 26 26 26 25 25 24 264 TWS 15 14 14 13 11 11 11 10 11 11 10 11 10 8 8 7 8 9 12 13 14 15 17 17 16 265 HID 6 5 6 266 SOL 710500430250110 0 30 0 0 0 0 0 0 0 30160330500650760830870870850720 267 """ 268 >>> len(kjfk.data.forecast) 269 25 270 >>> kjfk.data.forecast[0].snow_level 271 Number(repr='99', value=9900, spoken='nine nine hundred') 272 >>> print(kjfk.data.forecast[0].solar_radiation.value, kjfk.units.solar_radiation) 273 710 W/m2 274 ``` 275 276 The `parse` and `from_report` methods can parse a report string if you want 277 to override the normal fetching process. 278 ''' 279 280 report_type = "nbh" 281 _parser = staticmethod(parse_nbh)
Class to handle NBM NBH report data
Below is typical usage for fetching and pulling NBH data for KJFK.
>>> from avwx import Nbh
>>> kjfk = Nbh("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2020, 7, 26, 20, 37, 42, 352220, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
KJFK NBM V3.2 NBH GUIDANCE 7/26/2020 1900 UTC
UTC 20 21 22 23 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
TMP 90 89 88 87 85 84 82 81 81 80 79 78 77 76 76 78 81 84 87 89 90 91 92 92 93
DPT 69 68 66 66 65 65 65 66 66 66 66 67 67 66 67 67 67 67 67 68 67 67 67 67 67
SKY 9 14 41 58 61 71 66 55 39 37 39 43 40 38 29 21 19 26 24 27 22 14 22 26 26
WDR 22 22 23 24 25 25 25 25 25 25 25 26 26 26 26 26 26 26 25 24 24 23 23 22 23
WSP 10 9 9 8 7 6 5 5 5 6 5 5 5 5 4 4 5 5 7 8 8 9 10 10 10
GST 17 16 16 15 14 12 11 12 12 12 12 11 11 9 9 9 9 10 11 13 14 15 16 17 17
P01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
P06 0 0 0
Q01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
DUR 0
T01 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PZR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PSN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PPL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PRA 100100100100100100100100100100100100100100100100100100100100100100100100100
S01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
SLV 99101102103105105105106105104106104104104104103102100 99 98 98 99100101102
I01 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
CIG 888888888360360350340888888888888888888888888888888888888888888888888888888
VIS 110120120140140130120120120120110110110110110120130140150150150150150150150
LCB 80 60999999999999999999999999999999999999999999999999999999999999999999999
MHT 26 26 20 19 8 7 6 4 4 5 5 5 6 4 5 6 6 11 20 27 37 39 43 39 31
TWD 24 26 26 27 26 26 24 23 24 26 26 26 27 27 28 27 27 27 27 26 26 26 25 25 24
TWS 15 14 14 13 11 11 11 10 11 11 10 11 10 8 8 7 8 9 12 13 14 15 17 17 16
HID 6 5 6
SOL 710500430250110 0 30 0 0 0 0 0 0 0 30160330500650760830870870850720
"""
>>> len(kjfk.data.forecast)
25
>>> kjfk.data.forecast[0].snow_level
Number(repr='99', value=9900, spoken='nine nine hundred')
>>> print(kjfk.data.forecast[0].solar_radiation.value, kjfk.units.solar_radiation)
710 W/m2
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.forecast.nbm._Nbm
- units
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- parse
- sanitize
284class Nbs(_Nbm): 285 ''' 286 Class to handle NBM NBS report data 287 288 Below is typical usage for fetching and pulling Nbs data for KJFK. 289 290 ```python 291 >>> from avwx import Nbs 292 >>> kjfk = Nbs("KJFK") 293 >>> kjfk.station.name 294 'John F Kennedy International Airport' 295 >>> kjfk.update() 296 True 297 >>> kjfk.last_updated 298 datetime.datetime(2020, 7, 28, 1, 3, 46, 447635, tzinfo=datetime.timezone.utc) 299 >>> print(kjfk.raw) 300 """ 301 KJFK NBM V3.2 NBS GUIDANCE 7/27/2020 2300 UTC 302 DT /JULY 28 /JULY 29 /JULY 30 303 UTC 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21 304 FHR 04 07 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 305 N/X 79 93 76 91 76 306 TMP 85 82 80 83 89 91 89 84 81 79 77 80 85 89 87 83 81 79 77 80 86 88 86 307 DPT 70 70 71 72 72 72 73 72 72 71 69 69 68 67 68 69 68 67 68 69 68 69 70 308 SKY 4 10 2 4 12 23 38 61 53 62 51 26 19 9 21 24 25 34 32 45 57 70 79 309 WDR 23 24 23 24 24 22 23 27 28 28 34 35 21 20 19 22 23 25 26 26 23 20 20 310 WSP 8 8 5 6 8 9 7 5 3 2 1 2 3 6 9 7 4 4 3 3 4 7 8 311 GST 16 15 11 11 13 15 15 11 9 5 4 4 6 12 15 13 11 11 8 6 7 13 15 312 P06 0 1 15 48 17 11 8 8 1 0 5 313 P12 1 48 17 8 1 314 Q06 0 0 0 11 0 0 0 0 0 0 0 315 Q12 0 11 0 0 0 316 DUR 0 2 0 0 0 317 T03 2 3 1 1 2 10 27 30 21 13 8 5 1 0 2 3 4 3 2 3 1 3 7 318 T12 4 48 33 6 8 319 PZR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 320 PSN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 321 PPL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 322 PRA 100100100100100100100100100100100100100100100100100100100100100100100 323 S06 0 0 0 0 0 0 0 0 0 0 0 324 SLV 108112113133135139141139137134124113102 99107115114118118119118118118 325 I06 0 0 0 0 0 0 0 0 0 0 0 326 CIG 888888888888888888888170888150888888888888888888888888888888888888888 327 VIS 120120110120130130130110110110110130110110110120120120110110110110110 328 LCB 60 70999 90 90999 50 60 60 60 60 60 22999999200230 70 80 60 60150 60 329 MHT 7 6 5 13 30 46 19 15 7 7 10 16 40 35 13 4 4 4 9 18 31 27 13 330 TWD 23 24 23 27 25 24 27 27 28 21 34 6 26 23 20 19 26 23 27 29 24 20 18 331 TWS 17 16 9 11 13 16 12 9 5 5 4 3 4 8 11 10 8 10 10 6 6 9 13 332 HID 4 4 4 4 3 4 4 5 5 3 4 333 SOL 0 0 0320700760360100 0 0 0320720830620190 0 0 0230480570540 334 """ 335 >>> len(kjfk.data.forecast) 336 23 337 >>> kjfk.data.forecast[0].ceiling 338 Number(repr='888', value=None, spoken='unlimited') 339 >>> print(kjfk.data.forecast[7].precip_amount_12.value, kjfk.units.accumulation) 340 0.11 in 341 ``` 342 343 The `parse` and `from_report` methods can parse a report string if you want 344 to override the normal fetching process. 345 ''' 346 347 report_type = "nbs" 348 _parser = staticmethod(parse_nbs)
Class to handle NBM NBS report data
Below is typical usage for fetching and pulling Nbs data for KJFK.
>>> from avwx import Nbs
>>> kjfk = Nbs("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2020, 7, 28, 1, 3, 46, 447635, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
KJFK NBM V3.2 NBS GUIDANCE 7/27/2020 2300 UTC
DT /JULY 28 /JULY 29 /JULY 30
UTC 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21 00 03 06 09 12 15 18 21
FHR 04 07 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70
N/X 79 93 76 91 76
TMP 85 82 80 83 89 91 89 84 81 79 77 80 85 89 87 83 81 79 77 80 86 88 86
DPT 70 70 71 72 72 72 73 72 72 71 69 69 68 67 68 69 68 67 68 69 68 69 70
SKY 4 10 2 4 12 23 38 61 53 62 51 26 19 9 21 24 25 34 32 45 57 70 79
WDR 23 24 23 24 24 22 23 27 28 28 34 35 21 20 19 22 23 25 26 26 23 20 20
WSP 8 8 5 6 8 9 7 5 3 2 1 2 3 6 9 7 4 4 3 3 4 7 8
GST 16 15 11 11 13 15 15 11 9 5 4 4 6 12 15 13 11 11 8 6 7 13 15
P06 0 1 15 48 17 11 8 8 1 0 5
P12 1 48 17 8 1
Q06 0 0 0 11 0 0 0 0 0 0 0
Q12 0 11 0 0 0
DUR 0 2 0 0 0
T03 2 3 1 1 2 10 27 30 21 13 8 5 1 0 2 3 4 3 2 3 1 3 7
T12 4 48 33 6 8
PZR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PSN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PPL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PRA 100100100100100100100100100100100100100100100100100100100100100100100
S06 0 0 0 0 0 0 0 0 0 0 0
SLV 108112113133135139141139137134124113102 99107115114118118119118118118
I06 0 0 0 0 0 0 0 0 0 0 0
CIG 888888888888888888888170888150888888888888888888888888888888888888888
VIS 120120110120130130130110110110110130110110110120120120110110110110110
LCB 60 70999 90 90999 50 60 60 60 60 60 22999999200230 70 80 60 60150 60
MHT 7 6 5 13 30 46 19 15 7 7 10 16 40 35 13 4 4 4 9 18 31 27 13
TWD 23 24 23 27 25 24 27 27 28 21 34 6 26 23 20 19 26 23 27 29 24 20 18
TWS 17 16 9 11 13 16 12 9 5 5 4 3 4 8 11 10 8 10 10 6 6 9 13
HID 4 4 4 4 3 4 4 5 5 3 4
SOL 0 0 0320700760360100 0 0 0320720830620190 0 0 0230480570540
"""
>>> len(kjfk.data.forecast)
23
>>> kjfk.data.forecast[0].ceiling
Number(repr='888', value=None, spoken='unlimited')
>>> print(kjfk.data.forecast[7].precip_amount_12.value, kjfk.units.accumulation)
0.11 in
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.forecast.nbm._Nbm
- units
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- parse
- sanitize
410class Nbx(_Nbm): 411 ''' 412 Class to handle NBM NBX report data 413 414 Below is typical usage for fetching and pulling NBX data for KJFK. 415 416 ```python 417 >>> from avwx import Nbx 418 >>> kjfk = Nbx("KJFK") 419 >>> kjfk.station.name 420 'John F Kennedy International Airport' 421 >>> kjfk.update() 422 True 423 >>> kjfk.last_updated 424 datetime.datetime(2023, 10, 17, 4, 0, 0, 909939, tzinfo=datetime.timezone.utc) 425 >>> print(kjfk.raw) 426 """ 427 086092 NBM V4.1 NBX GUIDANCE 10/17/2023 0300 UTC 428 WED 25 |THU 26 |FRI 27 |SAT 28 429 UTC 12 |00 12 |00 12 |00 430 FHR 201|213 225|237 249|261 431 TXN 76| 81 75| 81 75| 81 432 XND 1| 1 1| 2 1| 0 433 TMP 77| 77 77| 78 76| 78 434 TSD 1| 2 1| 1 2| 1 435 DPT 67| 67 69| 68 70| 69 436 DSD 1| 2 1| 1 2| 1 437 SKY 34| 46 27| 50 26| 37 438 SSD 12| 22 13| 22 12| 7 439 WDR 7| 7 7| 6 5| 5 440 WSP 15| 16 15| 17 15| 17 441 WSD 3| 4 3| 3 4| 3 442 GST 22| 24 22| 25 22| 25 443 GSD 2| 2 2| 1 1| 1 444 P12 8| 8 9| 7 7| 7 445 Q12 0| 0 0| 3 0| 0 446 Q24 0| 0| 3| 447 DUR 0| 0 0| 0 0| 0 448 PZR 0| 0 0| 0 0| 0 449 PSN 0| 0 0| 0 0| 0 450 PPL 0| 0 0| 0 0| 0 451 PRA 7| 16 14| 10 6| 11 452 S12 0| 0 0| 0 0| 0 453 I12 0| 0 0| 0 0| 0 454 SOL 1| 35 1| 15 1| 38 455 """ 456 >>> len(kjfk.data.forecast) 457 25 458 >>> kjfk.data.forecast[0].wind_speed 459 Number(repr='15', value=150, spoken='one five zero') 460 >>> print(kjfk.data.forecast[1].solar_radiation.value, kjfk.units.solar_radiation) 461 35 W/m2 462 ``` 463 464 The `parse` and `from_report` methods can parse a report string if you want 465 to override the normal fetching process. 466 ''' 467 468 report_type = "nbx" 469 _parser = staticmethod(parse_nbx)
Class to handle NBM NBX report data
Below is typical usage for fetching and pulling NBX data for KJFK.
>>> from avwx import Nbx
>>> kjfk = Nbx("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2023, 10, 17, 4, 0, 0, 909939, tzinfo=datetime.timezone.utc)
>>> print(kjfk.raw)
"""
086092 NBM V4.1 NBX GUIDANCE 10/17/2023 0300 UTC
WED 25 |THU 26 |FRI 27 |SAT 28
UTC 12 |00 12 |00 12 |00
FHR 201|213 225|237 249|261
TXN 76| 81 75| 81 75| 81
XND 1| 1 1| 2 1| 0
TMP 77| 77 77| 78 76| 78
TSD 1| 2 1| 1 2| 1
DPT 67| 67 69| 68 70| 69
DSD 1| 2 1| 1 2| 1
SKY 34| 46 27| 50 26| 37
SSD 12| 22 13| 22 12| 7
WDR 7| 7 7| 6 5| 5
WSP 15| 16 15| 17 15| 17
WSD 3| 4 3| 3 4| 3
GST 22| 24 22| 25 22| 25
GSD 2| 2 2| 1 1| 1
P12 8| 8 9| 7 7| 7
Q12 0| 0 0| 3 0| 0
Q24 0| 0| 3|
DUR 0| 0 0| 0 0| 0
PZR 0| 0 0| 0 0| 0
PSN 0| 0 0| 0 0| 0
PPL 0| 0 0| 0 0| 0
PRA 7| 16 14| 10 6| 11
S12 0| 0 0| 0 0| 0
I12 0| 0 0| 0 0| 0
SOL 1| 35 1| 15 1| 38
"""
>>> len(kjfk.data.forecast)
25
>>> kjfk.data.forecast[0].wind_speed
Number(repr='15', value=150, spoken='one five zero')
>>> print(kjfk.data.forecast[1].solar_radiation.value, kjfk.units.solar_radiation)
35 W/m2
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
Inherited Members
- avwx.forecast.base.Forecast
- Forecast
- service
- avwx.forecast.nbm._Nbm
- units
- avwx.base.ManagedReport
- code
- station
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- data
- parse
- sanitize
67class Notams(Reports): 68 ''' 69 The Notams class provides two ways of requesting all applicable NOTAMs in 70 an area: airport code and coordinate. The service will fetch all reports 71 within 10 nautical miles of the desired center point. You can change the 72 distance by updating the `Notams.radius` member before calling `update()`. 73 74 ```python 75 >>> from pprint import pprint 76 >>> from avwx import Notams 77 >>> from avwx.structs import Coord 78 >>> 79 >>> kjfk = Notams("KJFK") 80 >>> kjfk.update() 81 True 82 >>> kjfk.last_updated 83 datetime.datetime(2022, 5, 26, 0, 43, 22, 44753, tzinfo=datetime.timezone.utc) 84 >>> print(kjfk.data[0].raw) 85 01/113 NOTAMN 86 Q) ZNY/QMXLC/IV/NBO/A/000/999/4038N07346W005 87 A) KJFK 88 B) 2101081328 89 C) 2209301100 90 91 E) TWY TB BTN TERMINAL 8 RAMP AND TWY A CLSD 92 >>> pprint(kjfk.data[0].qualifiers) 93 Qualifiers(repr='ZNY/QMXLC/IV/NBO/A/000/999/4038N07346W005', 94 fir='ZNY', 95 subject=Code(repr='MX', value='Taxiway'), 96 condition=Code(repr='LC', value='Closed'), 97 traffic=Code(repr='IV', value='IFR and VFR'), 98 purpose=[Code(repr='N', value='Immediate'), 99 Code(repr='B', value='Briefing'), 100 Code(repr='O', value='Flight Operations')], 101 scope=[Code(repr='A', value='Aerodrome')], 102 lower=Number(repr='000', value=0, spoken='zero'), 103 upper=Number(repr='999', value=999, spoken='nine nine nine'), 104 coord=Coord(lat=40.38, lon=-73.46, repr='4038N07346W'), 105 radius=Number(repr='005', value=5, spoken='five')) 106 >>> 107 >>> coord = Notams(coord=Coord(lat=52, lon=-0.23)) 108 >>> coord.update() 109 True 110 >>> coord.data[0].station 111 'EGSS' 112 >>> print(coord.data[0].body) 113 LONDON STANSTED ATC SURVEILLANCE MINIMUM ALTITUDE CHART - IN 114 FREQUENCY BOX RENAME ESSEX RADAR TO STANSTED RADAR. 115 UK AIP AD 2.EGSS-5-1 REFERS 116 ``` 117 118 The `parse` and `from_report` methods can parse a report string if you want 119 to override the normal fetching process. 120 121 ```python 122 >>> from avwx import Notams 123 >>> report = """ 124 05/295 NOTAMR 125 Q) ZNY/QMNHW/IV/NBO/A/000/999/4038N07346W005 126 A) KJFK 127 B) 2205201527 128 C) 2205271100 129 130 E) APRON TERMINAL 4 RAMP CONST WIP S SIDE TAXILANE G LGTD AND BARRICADED 131 """ 132 >>> kjfk = Notams.from_report(report) 133 >>> kjfk.data[0].type 134 Code(repr='NOTAMR', value='Replace') 135 >>> kjfk.data[0].start_time 136 Timestamp(repr='2205201527', dt=datetime.datetime(2022, 5, 20, 15, 27, tzinfo=datetime.timezone.utc)) 137 ``` 138 ''' 139 140 data: Optional[List[NotamData]] = None # type: ignore 141 radius: int = 10 142 143 def __init__(self, code: Optional[str] = None, coord: Optional[Coord] = None): 144 super().__init__(code, coord) 145 self.service = FAA_NOTAM("notam") 146 147 async def _post_update(self) -> None: 148 self._post_parse() 149 150 def _post_parse(self) -> None: 151 self.data, units = [], None 152 if self.raw is None: 153 return 154 for report in self.raw: 155 if "||" in report: 156 issue_text, report = report.split("||") 157 issued_value = datetime.strptime(issue_text, r"%m/%d/%Y %H%M") 158 issued = Timestamp(issue_text, issued_value) 159 else: 160 issued = None 161 try: 162 data, units = parse(report, issued=issued) 163 self.data.append(data) 164 except Exception as exc: # pylint: disable=broad-except 165 exceptions.exception_intercept(exc, raw=report) # type: ignore 166 if units: 167 self.units = units 168 169 @staticmethod 170 def sanitize(report: str) -> str: 171 """Sanitizes a NOTAM string""" 172 return sanitize(report) 173 174 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 175 """Async updates report data by fetching and parsing the report""" 176 reports = await self.service.async_fetch( # type: ignore 177 icao=self.code, coord=self.coord, radius=self.radius, timeout=timeout 178 ) 179 self.source = self.service.root 180 return await self._update(reports, None, disable_post)
The Notams class provides two ways of requesting all applicable NOTAMs in
an area: airport code and coordinate. The service will fetch all reports
within 10 nautical miles of the desired center point. You can change the
distance by updating the Notams.radius
member before calling update()
.
>>> from pprint import pprint
>>> from avwx import Notams
>>> from avwx.structs import Coord
>>>
>>> kjfk = Notams("KJFK")
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2022, 5, 26, 0, 43, 22, 44753, tzinfo=datetime.timezone.utc)
>>> print(kjfk.data[0].raw)
01/113 NOTAMN
Q) ZNY/QMXLC/IV/NBO/A/000/999/4038N07346W005
A) KJFK
B) 2101081328
C) 2209301100
E) TWY TB BTN TERMINAL 8 RAMP AND TWY A CLSD
>>> pprint(kjfk.data[0].qualifiers)
Qualifiers(repr='ZNY/QMXLC/IV/NBO/A/000/999/4038N07346W005',
fir='ZNY',
subject=Code(repr='MX', value='Taxiway'),
condition=Code(repr='LC', value='Closed'),
traffic=Code(repr='IV', value='IFR and VFR'),
purpose=[Code(repr='N', value='Immediate'),
Code(repr='B', value='Briefing'),
Code(repr='O', value='Flight Operations')],
scope=[Code(repr='A', value='Aerodrome')],
lower=Number(repr='000', value=0, spoken='zero'),
upper=Number(repr='999', value=999, spoken='nine nine nine'),
coord=Coord(lat=40.38, lon=-73.46, repr='4038N07346W'),
radius=Number(repr='005', value=5, spoken='five'))
>>>
>>> coord = Notams(coord=Coord(lat=52, lon=-0.23))
>>> coord.update()
True
>>> coord.data[0].station
'EGSS'
>>> print(coord.data[0].body)
LONDON STANSTED ATC SURVEILLANCE MINIMUM ALTITUDE CHART - IN
FREQUENCY BOX RENAME ESSEX RADAR TO STANSTED RADAR.
UK AIP AD 2.EGSS-5-1 REFERS
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
>>> from avwx import Notams
>>> report = """
05/295 NOTAMR
Q) ZNY/QMNHW/IV/NBO/A/000/999/4038N07346W005
A) KJFK
B) 2205201527
C) 2205271100
E) APRON TERMINAL 4 RAMP CONST WIP S SIDE TAXILANE G LGTD AND BARRICADED
"""
>>> kjfk = Notams.from_report(report)
>>> kjfk.data[0].type
Code(repr='NOTAMR', value='Replace')
>>> kjfk.data[0].start_time
Timestamp(repr='2205201527', dt=datetime.datetime(2022, 5, 20, 15, 27, tzinfo=datetime.timezone.utc))
169 @staticmethod 170 def sanitize(report: str) -> str: 171 """Sanitizes a NOTAM string""" 172 return sanitize(report)
Sanitizes a NOTAM string
174 async def async_update(self, timeout: int = 10, disable_post: bool = False) -> bool: 175 """Async updates report data by fetching and parsing the report""" 176 reports = await self.service.async_fetch( # type: ignore 177 icao=self.code, coord=self.coord, radius=self.radius, timeout=timeout 178 ) 179 self.source = self.service.root 180 return await self._update(reports, None, disable_post)
Async updates report data by fetching and parsing the report
Inherited Members
- avwx.current.base.Reports
- coord
- raw
- units
- sanitization
- parse
- async_parse
- update
- avwx.base.ManagedReport
- code
- station
- from_report
- avwx.base.AVWXBase
- last_updated
- issued
- source
40class Pireps(Reports): 41 """ 42 The Pireps class offers an object-oriented approach to managing multiple 43 PIREP reports for a single station. 44 45 Below is typical usage for fetching and pulling PIREP data for KJFK. 46 47 ```python 48 >>> from avwx import Pireps 49 >>> kmco = Pireps("KMCO") 50 >>> kmco.station.name 51 'Orlando International Airport' 52 >>> kmco.update() 53 True 54 >>> kmco.last_updated 55 datetime.datetime(2019, 5, 24, 13, 31, 46, 561732, tzinfo=datetime.timezone.utc) 56 >>> kmco.raw[0] 57 'FLL UA /OV KFLL275015/TM 1241/FL020/TP B737/SK TOP020/RM DURD RY10L' 58 >>> kmco.data[0].location 59 Location(repr='KFLL275015', station='KFLL', direction=Number(repr='275', value=275, spoken='two seven five'), distance=Number(repr='015', value=15, spoken='one five')) 60 ``` 61 62 The `parse` and `from_report` methods can parse a report string if you want 63 to override the normal fetching process. 64 """ 65 66 data: Optional[List[Optional[PirepData]]] = None # type: ignore 67 sanitization: Optional[List[Optional[Sanitization]]] = None # type: ignore 68 69 def __init__(self, code: Optional[str] = None, coord: Optional[Coord] = None): 70 super().__init__(code, coord) 71 self.service = NOAA_ScrapeList("pirep") 72 73 @staticmethod 74 def _report_filter(reports: List[str]) -> List[str]: 75 """Removes AIREPs before updating raw_reports""" 76 return [r for r in reports if not r.startswith("ARP")] 77 78 async def _post_update(self) -> None: 79 self.data, self.sanitization = [], [] 80 if self.raw is None: 81 return 82 for report in self.raw: 83 try: 84 data, sans = parse(report, issued=self.issued) 85 self.data.append(data) 86 self.sanitization.append(sans) 87 except Exception as exc: # pylint: disable=broad-except 88 exceptions.exception_intercept(exc, raw=report) # type: ignore 89 90 def _post_parse(self) -> None: 91 self.data, self.sanitization = [], [] 92 if self.raw is None: 93 return 94 for report in self.raw: 95 data, sans = parse(report, issued=self.issued) 96 self.data.append(data) 97 self.sanitization.append(sans) 98 99 @staticmethod 100 def sanitize(report: str) -> str: 101 """Sanitizes a PIREP string""" 102 return sanitize(report)[0]
The Pireps class offers an object-oriented approach to managing multiple PIREP reports for a single station.
Below is typical usage for fetching and pulling PIREP data for KJFK.
>>> from avwx import Pireps
>>> kmco = Pireps("KMCO")
>>> kmco.station.name
'Orlando International Airport'
>>> kmco.update()
True
>>> kmco.last_updated
datetime.datetime(2019, 5, 24, 13, 31, 46, 561732, tzinfo=datetime.timezone.utc)
>>> kmco.raw[0]
'FLL UA /OV KFLL275015/TM 1241/FL020/TP B737/SK TOP020/RM DURD RY10L'
>>> kmco.data[0].location
Location(repr='KFLL275015', station='KFLL', direction=Number(repr='275', value=275, spoken='two seven five'), distance=Number(repr='015', value=15, spoken='one five'))
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
99 @staticmethod 100 def sanitize(report: str) -> str: 101 """Sanitizes a PIREP string""" 102 return sanitize(report)[0]
Sanitizes a PIREP string
Inherited Members
- avwx.current.base.Reports
- coord
- raw
- units
- parse
- async_parse
- update
- async_update
- avwx.base.ManagedReport
- code
- station
- from_report
- avwx.base.AVWXBase
- last_updated
- issued
- source
53@dataclass 54class Station: 55 """ 56 The Station dataclass stores basic info about the desired station and 57 available Runways. 58 59 The easiest way to get a station is to supply the ICAO, IATA, or GPS code. 60 The example below uses `from_code` which checks against all three types, 61 but you can also use `from_icao`, `from_iata`, or `from_gps` if you know 62 what type of code you are using. This can be important if you may be using 63 a code used by more than one station depending on the context. ICAO and 64 IATA codes are guarenteed unique, but not all airports have them. That 65 said, all stations available in AVWX have either an ICAO or GPS code. 66 67 ```python 68 >>> from avwx import Station 69 >>> klex = Station.from_code("KLEX") 70 >>> f"{klex.name} in {klex.city}, {klex.state}" 71 'Blue Grass Airport in Lexington, KY' 72 >>> coord = round(klex.latitude, 3), round(klex.longitude, 3) 73 >>> f"Located at {coord} at {klex.elevation_ft} feet ({klex.elevation_m} meters)" 74 'Located at (38.036, -84.606) at 979 feet (298 meters)' 75 >>> rw = max(klex.runways, key=lambda r: r.length_ft) 76 >>> f"Its longest runway is {rw.ident1}/{rw.ident2} at {rw.length_ft} feet" 77 'Its longest runway is 04/22 at 7003 feet' 78 ``` 79 80 This is also the same information you'd get from calling Report.station. 81 82 ```python 83 >>> from avwx import Metar 84 >>> klex = Metar('KLEX') 85 >>> klex.station.name 86 'Blue Grass Airport' 87 ``` 88 """ 89 90 # pylint: disable=too-many-instance-attributes 91 92 city: Optional[str] 93 country: str 94 elevation_ft: Optional[int] 95 elevation_m: Optional[int] 96 gps: Optional[str] 97 iata: Optional[str] 98 icao: Optional[str] 99 latitude: float 100 local: Optional[str] 101 longitude: float 102 name: str 103 note: Optional[str] 104 reporting: bool 105 runways: List[Runway] 106 state: Optional[str] 107 type: str 108 website: Optional[str] 109 wiki: Optional[str] 110 111 @classmethod 112 def _from_code(cls: Type[T], ident: str) -> T: 113 try: 114 info: Dict[str, Any] = copy(STATIONS[ident]) 115 if info["runways"]: 116 info["runways"] = [Runway(**r) for r in info["runways"]] 117 return cls(**info) 118 except (KeyError, AttributeError) as not_found: 119 raise BadStation( 120 f"Could not find station with ident {ident}" 121 ) from not_found 122 123 @classmethod 124 def from_code(cls: Type[T], ident: str) -> T: 125 """Load a Station from ICAO, GPS, or IATA code in that order""" 126 if ident and isinstance(ident, str): 127 if len(ident) == 4: 128 with suppress(BadStation): 129 return cls.from_icao(ident) 130 with suppress(BadStation): 131 return cls.from_gps(ident) 132 if len(ident) == 3: 133 with suppress(BadStation): 134 return cls.from_iata(ident) 135 with suppress(BadStation): 136 return cls.from_local(ident) 137 raise BadStation(f"Could not find station with ident {ident}") 138 139 @classmethod 140 def from_icao(cls: Type[T], ident: str) -> T: 141 """Load a Station from an ICAO station ident""" 142 try: 143 return cls._from_code(_ICAO.value[ident.upper()]) 144 except (KeyError, AttributeError) as not_found: 145 raise BadStation( 146 f"Could not find station with ICAO ident {ident}" 147 ) from not_found 148 149 @classmethod 150 def from_iata(cls: Type[T], ident: str) -> T: 151 """Load a Station from an IATA code""" 152 try: 153 return cls._from_code(_IATA.value[ident.upper()]) 154 except (KeyError, AttributeError) as not_found: 155 raise BadStation( 156 f"Could not find station with IATA ident {ident}" 157 ) from not_found 158 159 @classmethod 160 def from_gps(cls: Type[T], ident: str) -> T: 161 """Load a Station from a GPS code""" 162 try: 163 return cls._from_code(_GPS.value[ident.upper()]) 164 except (KeyError, AttributeError) as not_found: 165 raise BadStation( 166 f"Could not find station with GPS ident {ident}" 167 ) from not_found 168 169 @classmethod 170 def from_local(cls: Type[T], ident: str) -> T: 171 """Load a Station from a local code""" 172 try: 173 return cls._from_code(_LOCAL.value[ident.upper()]) 174 except (KeyError, AttributeError) as not_found: 175 raise BadStation( 176 f"Could not find station with local ident {ident}" 177 ) from not_found 178 179 @classmethod 180 def nearest( 181 cls: Type[T], 182 lat: Optional[float] = None, 183 lon: Optional[float] = None, 184 is_airport: bool = False, 185 sends_reports: bool = True, 186 max_coord_distance: float = 10, 187 ) -> Optional[Tuple[T, dict]]: 188 """Load the Station nearest to your location or a lat,lon coordinate pair 189 190 Returns the Station and distances from source 191 192 NOTE: Becomes less accurate toward poles and doesn't cross +/-180 193 """ 194 if not (lat and lon): 195 lat, lon = _get_ip_location().pair 196 ret = nearest(lat, lon, 1, is_airport, sends_reports, max_coord_distance) 197 if not isinstance(ret, dict): 198 return None 199 station = ret.pop("station") 200 return station, ret 201 202 @property 203 def lookup_code(self) -> str: 204 """Returns the ICAO or GPS code for report fetch""" 205 if self.icao: 206 return self.icao 207 if self.gps: 208 return self.gps 209 raise BadStation("Station does not have a valid lookup code") 210 211 @property 212 def storage_code(self) -> str: 213 """Returns the first unique-ish code from what's available""" 214 if self.icao: 215 return self.icao 216 if self.iata: 217 return self.iata 218 if self.gps: 219 return self.gps 220 if self.local: 221 return self.local 222 raise BadStation("Station does not have any useable codes") 223 224 @property 225 def sends_reports(self) -> bool: 226 """Returns whether or not a Station likely sends weather reports""" 227 return self.reporting is True 228 229 @property 230 def coord(self) -> Coord: 231 """Returns the station location as a Coord""" 232 return Coord(lat=self.latitude, lon=self.longitude, repr=self.icao) 233 234 def distance(self, lat: float, lon: float) -> Distance: 235 """Returns a geopy Distance using the great circle method""" 236 return great_circle((lat, lon), (self.latitude, self.longitude)) 237 238 def nearby( 239 self, 240 is_airport: bool = False, 241 sends_reports: bool = True, 242 max_coord_distance: float = 10, 243 ) -> List[Tuple[T, dict]]: 244 """Returns Stations nearest to current station and their distances 245 246 NOTE: Becomes less accurate toward poles and doesn't cross +/-180 247 """ 248 stations = nearest( 249 self.latitude, 250 self.longitude, 251 11, 252 is_airport, 253 sends_reports, 254 max_coord_distance, 255 ) 256 if isinstance(stations, dict): 257 return [] 258 return [(s.pop("station"), s) for s in stations[1:]]
The Station dataclass stores basic info about the desired station and available Runways.
The easiest way to get a station is to supply the ICAO, IATA, or GPS code.
The example below uses from_code
which checks against all three types,
but you can also use from_icao
, from_iata
, or from_gps
if you know
what type of code you are using. This can be important if you may be using
a code used by more than one station depending on the context. ICAO and
IATA codes are guarenteed unique, but not all airports have them. That
said, all stations available in AVWX have either an ICAO or GPS code.
>>> from avwx import Station
>>> klex = Station.from_code("KLEX")
>>> f"{klex.name} in {klex.city}, {klex.state}"
'Blue Grass Airport in Lexington, KY'
>>> coord = round(klex.latitude, 3), round(klex.longitude, 3)
>>> f"Located at {coord} at {klex.elevation_ft} feet ({klex.elevation_m} meters)"
'Located at (38.036, -84.606) at 979 feet (298 meters)'
>>> rw = max(klex.runways, key=lambda r: r.length_ft)
>>> f"Its longest runway is {rw.ident1}/{rw.ident2} at {rw.length_ft} feet"
'Its longest runway is 04/22 at 7003 feet'
This is also the same information you'd get from calling Report.station.
>>> from avwx import Metar
>>> klex = Metar('KLEX')
>>> klex.station.name
'Blue Grass Airport'
123 @classmethod 124 def from_code(cls: Type[T], ident: str) -> T: 125 """Load a Station from ICAO, GPS, or IATA code in that order""" 126 if ident and isinstance(ident, str): 127 if len(ident) == 4: 128 with suppress(BadStation): 129 return cls.from_icao(ident) 130 with suppress(BadStation): 131 return cls.from_gps(ident) 132 if len(ident) == 3: 133 with suppress(BadStation): 134 return cls.from_iata(ident) 135 with suppress(BadStation): 136 return cls.from_local(ident) 137 raise BadStation(f"Could not find station with ident {ident}")
Load a Station from ICAO, GPS, or IATA code in that order
139 @classmethod 140 def from_icao(cls: Type[T], ident: str) -> T: 141 """Load a Station from an ICAO station ident""" 142 try: 143 return cls._from_code(_ICAO.value[ident.upper()]) 144 except (KeyError, AttributeError) as not_found: 145 raise BadStation( 146 f"Could not find station with ICAO ident {ident}" 147 ) from not_found
Load a Station from an ICAO station ident
149 @classmethod 150 def from_iata(cls: Type[T], ident: str) -> T: 151 """Load a Station from an IATA code""" 152 try: 153 return cls._from_code(_IATA.value[ident.upper()]) 154 except (KeyError, AttributeError) as not_found: 155 raise BadStation( 156 f"Could not find station with IATA ident {ident}" 157 ) from not_found
Load a Station from an IATA code
159 @classmethod 160 def from_gps(cls: Type[T], ident: str) -> T: 161 """Load a Station from a GPS code""" 162 try: 163 return cls._from_code(_GPS.value[ident.upper()]) 164 except (KeyError, AttributeError) as not_found: 165 raise BadStation( 166 f"Could not find station with GPS ident {ident}" 167 ) from not_found
Load a Station from a GPS code
169 @classmethod 170 def from_local(cls: Type[T], ident: str) -> T: 171 """Load a Station from a local code""" 172 try: 173 return cls._from_code(_LOCAL.value[ident.upper()]) 174 except (KeyError, AttributeError) as not_found: 175 raise BadStation( 176 f"Could not find station with local ident {ident}" 177 ) from not_found
Load a Station from a local code
179 @classmethod 180 def nearest( 181 cls: Type[T], 182 lat: Optional[float] = None, 183 lon: Optional[float] = None, 184 is_airport: bool = False, 185 sends_reports: bool = True, 186 max_coord_distance: float = 10, 187 ) -> Optional[Tuple[T, dict]]: 188 """Load the Station nearest to your location or a lat,lon coordinate pair 189 190 Returns the Station and distances from source 191 192 NOTE: Becomes less accurate toward poles and doesn't cross +/-180 193 """ 194 if not (lat and lon): 195 lat, lon = _get_ip_location().pair 196 ret = nearest(lat, lon, 1, is_airport, sends_reports, max_coord_distance) 197 if not isinstance(ret, dict): 198 return None 199 station = ret.pop("station") 200 return station, ret
Load the Station nearest to your location or a lat,lon coordinate pair
Returns the Station and distances from source
NOTE: Becomes less accurate toward poles and doesn't cross +/-180
202 @property 203 def lookup_code(self) -> str: 204 """Returns the ICAO or GPS code for report fetch""" 205 if self.icao: 206 return self.icao 207 if self.gps: 208 return self.gps 209 raise BadStation("Station does not have a valid lookup code")
Returns the ICAO or GPS code for report fetch
211 @property 212 def storage_code(self) -> str: 213 """Returns the first unique-ish code from what's available""" 214 if self.icao: 215 return self.icao 216 if self.iata: 217 return self.iata 218 if self.gps: 219 return self.gps 220 if self.local: 221 return self.local 222 raise BadStation("Station does not have any useable codes")
Returns the first unique-ish code from what's available
224 @property 225 def sends_reports(self) -> bool: 226 """Returns whether or not a Station likely sends weather reports""" 227 return self.reporting is True
Returns whether or not a Station likely sends weather reports
229 @property 230 def coord(self) -> Coord: 231 """Returns the station location as a Coord""" 232 return Coord(lat=self.latitude, lon=self.longitude, repr=self.icao)
Returns the station location as a Coord
234 def distance(self, lat: float, lon: float) -> Distance: 235 """Returns a geopy Distance using the great circle method""" 236 return great_circle((lat, lon), (self.latitude, self.longitude))
Returns a geopy Distance using the great circle method
238 def nearby( 239 self, 240 is_airport: bool = False, 241 sends_reports: bool = True, 242 max_coord_distance: float = 10, 243 ) -> List[Tuple[T, dict]]: 244 """Returns Stations nearest to current station and their distances 245 246 NOTE: Becomes less accurate toward poles and doesn't cross +/-180 247 """ 248 stations = nearest( 249 self.latitude, 250 self.longitude, 251 11, 252 is_airport, 253 sends_reports, 254 max_coord_distance, 255 ) 256 if isinstance(stations, dict): 257 return [] 258 return [(s.pop("station"), s) for s in stations[1:]]
Returns Stations nearest to current station and their distances
NOTE: Becomes less accurate toward poles and doesn't cross +/-180
35class Taf(Report): 36 """ 37 The Taf class offers an object-oriented approach to managing TAF data for a 38 single station. 39 40 ```python 41 >>> from avwx import Taf 42 >>> kjfk = Taf("KJFK") 43 >>> kjfk.station.name 44 'John F Kennedy International Airport' 45 >>> kjfk.update() 46 True 47 >>> kjfk.last_updated 48 datetime.datetime(2018, 3, 4, 23, 43, 26, 209644, tzinfo=datetime.timezone.utc) 49 >>> kjfk.raw 50 'KJFK 042030Z 0421/0524 33016G27KT P6SM BKN045 FM051600 36016G22KT P6SM BKN040 FM052100 35013KT P6SM SCT035' 51 >>> len(kjfk.data.forecast) 52 3 53 >>> kjfk.data.forecast[0].flight_rules 54 'VFR' 55 >>> kjfk.translations.forecast[0].wind 56 'NNW-330 at 16kt gusting to 27kt' 57 >>> kjfk.speech 58 'Starting on March 4th - From 21 to 16 zulu, Winds three three zero at 16kt gusting to 27kt. Visibility greater than six miles. Broken layer at 4500ft. From 16 to 21 zulu, Winds three six zero at 16kt gusting to 22kt. Visibility greater than six miles. Broken layer at 4000ft. From 21 to midnight zulu, Winds three five zero at 13kt. Visibility greater than six miles. Scattered clouds at 3500ft' 59 ``` 60 61 The `parse` and `from_report` methods can parse a report string if you want 62 to override the normal fetching process. 63 64 ```python 65 >>> from avwx import Taf 66 >>> report = "TAF ZYHB 082300Z 0823/0911 VRB03KT 9999 SCT018 BKN120 TX14/0907Z TN04/0921Z FM090100 09015KT 9999 -SHRA WS020/13045KT SCT018 BKN120 BECMG 0904/0906 34008KT PROB30 TEMPO 0906/0911 7000 -RA SCT020 650104 530804 RMK FCST BASED ON AUTO OBS. NXT FCST BY 090600Z" 67 >>> zyhb = Taf.from_report(report) 68 True 69 >>> zyhb.station.city 70 'Hulan' 71 >>> zyhb.data.remarks 72 'RMK FCST BASED ON AUTO OBS. NXT FCST BY 090600Z' 73 >>> zyhb.summary[-1] 74 'Vis 7km, Light Rain, Scattered clouds at 2000ft, Frequent moderate turbulence in clear air from 8000ft to 12000ft, Moderate icing in clouds from 1000ft to 5000ft' 75 ``` 76 """ 77 78 data: Optional[TafData] = None 79 translations: Optional[TafTrans] = None # type: ignore 80 81 async def _post_update(self) -> None: 82 if self.code is None or self.raw is None: 83 return 84 self.data, self.units, self.sanitization = parse( 85 self.code, self.raw, self.issued 86 ) 87 if self.data is None or self.units is None: 88 return 89 self.translations = translate_taf(self.data, self.units) 90 91 def _post_parse(self) -> None: 92 if self.code is None or self.raw is None: 93 return 94 self.data, self.units, self.sanitization = parse( 95 self.code, self.raw, self.issued 96 ) 97 if self.data is None or self.units is None: 98 return 99 self.translations = translate_taf(self.data, self.units) 100 101 @property 102 def summary(self) -> List[str]: 103 """Condensed summary for each forecast created from translations""" 104 if not self.translations: 105 self.update() 106 if self.translations is None or self.translations.forecast is None: 107 return [] 108 return [summary.taf(trans) for trans in self.translations.forecast] 109 110 @property 111 def speech(self) -> Optional[str]: 112 """Report summary designed to be read by a text-to-speech program""" 113 if not self.data: 114 self.update() 115 if self.data is None or self.units is None: 116 return None 117 return speech.taf(self.data, self.units)
The Taf class offers an object-oriented approach to managing TAF data for a single station.
>>> from avwx import Taf
>>> kjfk = Taf("KJFK")
>>> kjfk.station.name
'John F Kennedy International Airport'
>>> kjfk.update()
True
>>> kjfk.last_updated
datetime.datetime(2018, 3, 4, 23, 43, 26, 209644, tzinfo=datetime.timezone.utc)
>>> kjfk.raw
'KJFK 042030Z 0421/0524 33016G27KT P6SM BKN045 FM051600 36016G22KT P6SM BKN040 FM052100 35013KT P6SM SCT035'
>>> len(kjfk.data.forecast)
3
>>> kjfk.data.forecast[0].flight_rules
'VFR'
>>> kjfk.translations.forecast[0].wind
'NNW-330 at 16kt gusting to 27kt'
>>> kjfk.speech
'Starting on March 4th - From 21 to 16 zulu, Winds three three zero at 16kt gusting to 27kt. Visibility greater than six miles. Broken layer at 4500ft. From 16 to 21 zulu, Winds three six zero at 16kt gusting to 22kt. Visibility greater than six miles. Broken layer at 4000ft. From 21 to midnight zulu, Winds three five zero at 13kt. Visibility greater than six miles. Scattered clouds at 3500ft'
The parse
and from_report
methods can parse a report string if you want
to override the normal fetching process.
>>> from avwx import Taf
>>> report = "TAF ZYHB 082300Z 0823/0911 VRB03KT 9999 SCT018 BKN120 TX14/0907Z TN04/0921Z FM090100 09015KT 9999 -SHRA WS020/13045KT SCT018 BKN120 BECMG 0904/0906 34008KT PROB30 TEMPO 0906/0911 7000 -RA SCT020 650104 530804 RMK FCST BASED ON AUTO OBS. NXT FCST BY 090600Z"
>>> zyhb = Taf.from_report(report)
True
>>> zyhb.station.city
'Hulan'
>>> zyhb.data.remarks
'RMK FCST BASED ON AUTO OBS. NXT FCST BY 090600Z'
>>> zyhb.summary[-1]
'Vis 7km, Light Rain, Scattered clouds at 2000ft, Frequent moderate turbulence in clear air from 8000ft to 12000ft, Moderate icing in clouds from 1000ft to 5000ft'
101 @property 102 def summary(self) -> List[str]: 103 """Condensed summary for each forecast created from translations""" 104 if not self.translations: 105 self.update() 106 if self.translations is None or self.translations.forecast is None: 107 return [] 108 return [summary.taf(trans) for trans in self.translations.forecast]
Condensed summary for each forecast created from translations
110 @property 111 def speech(self) -> Optional[str]: 112 """Report summary designed to be read by a text-to-speech program""" 113 if not self.data: 114 self.update() 115 if self.data is None or self.units is None: 116 return None 117 return speech.taf(self.data, self.units)
Report summary designed to be read by a text-to-speech program
Inherited Members
- avwx.current.base.Report
- Report
- sanitization
- avwx.base.ManagedReport
- code
- station
- service
- from_report
- update
- async_update
- avwx.base.AVWXBase
- last_updated
- issued
- source
- raw
- units
- parse
- sanitize