-
Notifications
You must be signed in to change notification settings - Fork 22
/
notam.py
89 lines (79 loc) · 5.47 KB
/
notam.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import timeutils
import _abbr
import re as _re
import _parser
from io import StringIO as _StringIO
class Notam(object):
def __init__(self):
self.full_text = None # The full text of the NOTAM (for example, when constructed with from_str(s),
# this will contain s.
self.notam_id = None # The series and number/year of this NOTAM.
self.notam_type = None # The NOTAM type: 'NEW', 'REPLACE', or 'CANCEL'.
self.ref_notam_id = None # If this NOTAM references a previous NOTAM (notam_type is 'REPLACE' or 'CANCEL'),
# the series and number/year of the other NOTAM.
self.fir = None # The FIR within which the subject of the information is located.
self.notam_code = None # The five-letter NOTAM code, beginning with 'Q'. (Currently a simple str; at some
# point may be further parsed to specify the code's meaning.)
self.traffic_type = None # Set of affected traffic. Will contain one or more of:
# 'IFR'/'VFR'/'CHECKLIST'.
self.purpose = None # Set of NOTAM purposes. Will contain one or more of:
# 'IMMEDIATE ATTENTION'/'OPERATIONAL SIGNIFICANCE'/'FLIGHT OPERATIONS'/
# 'MISC'/'CHECKLIST'.
self.scope = None # Set of NOTAM scopes. Will contain one or more of:
# 'AERODROME'/'EN-ROUTE'/'NAV WARNING'/'CHECKLIST'.
self.fl_lower = None # Lower vertical limit of NOTAM area of influence, expressed in flight levels (int).
self.fl_upper = None # Upper vertical limit of NOTAM area of influence, expressed in flight levels (int).
self.area = None # Approximate circle whose radius encompasses the NOTAM's whole area of influence.
# This is a dict with keys: 'lat', 'long', 'radius' (str, str, int respectively).
self.location = None # List of one or more ICAO location indicators, specifying the aerodrome or FIR
# in which the facility, airspace, or condition being reported on is located.
self.valid_from = None # The date and time at which the NOTAM comes into force (datetime.datetime).
self.valid_till = None # For anything except a 'CANCEL'-type NOTAM, a date and time indicating duration of
# information (datetime.datetime). If permanent, equal to datetime.datetime.max.
# If the validity period is estimated, an instance of timeutils.EstimatedDateTime
# with an attribute 'is_estimated' set to True.
self.schedule = None # If the condition is active in accordance with a specific time date schedule, an
# abbreviated textual description of this schedule.
self.body = None # Text of NOTAM; Plain-language Entry (using ICAO Abbreviations).
self.limit_lower = None # Textual specification of lower height limit of activities or restrictions.
self.limit_upper = None # Textual specification of upper height limits of activities or restrictions.
# The following contain [start,end) indices for their corresponding NOTAM items (if such exist).
# They can be used to index into Notam.full_text.
self.indices_item_a = None
self.indices_item_b = None
self.indices_item_c = None
self.indices_item_d = None
self.indices_item_e = None
self.indices_item_f = None
self.indices_item_g = None
def decoded(self):
"""Returns the full text of the NOTAM, with ICAO abbreviations decoded into their un-abbreviated
form where appropriate."""
with _StringIO() as sb:
indices = [getattr(self, 'indices_item_{}'.format(i)) for i in ('d', 'e', 'f', 'g')]
indices = [i for i in indices if i is not None]
indices.sort() # The items should already be listed in the order of their apperance in the text, but
# we sort them here just in case
indices = [(0, 0)] + indices + [(-1, -1)]
for (cur, nxt) in zip(indices, indices[1:]):
(cs, ce) = cur
(ns, ne) = nxt
sb.write(self.decode_abbr(self.full_text[cs:ce])) # decode the text of this range
sb.write(self.full_text[ce:ns]) # copy the text from end of current range to start
# of next verbatim
return sb.getvalue()
@staticmethod
def from_str(s):
"""Returns a Notam containing information parsed from within the provided string."""
n = Notam()
visitor = _parser.NotamParseVisitor(n)
visitor.parse(s)
return n
@staticmethod
def decode_abbr(txt):
"""Decodes ICAO abbreviations in 'txt' to their un-abbreviated form."""
if not getattr(Notam.decode_abbr, 'regex', False):
Notam.decode_abbr.regex = _re.compile(r'\b(' +
'|'.join([_re.escape(key) for key in _abbr.ICAO_abbr.keys()]) +
r')\b')
return Notam.decode_abbr.regex.sub(lambda m: _abbr.ICAO_abbr[m.group()], txt)