Fix multi-line polygons
This commit is contained in:
parent
77804f5e1d
commit
584642b61c
2 changed files with 68 additions and 30 deletions
|
@ -30,6 +30,8 @@ RE_PRODUCT = re.compile(r'^(?P<product>[A-Z]{3})(?P<wfo>[A-Z]{3})$')
|
|||
|
||||
RE_POLY = re.compile(r'^LAT\.\.\.LON (?P<coords>\d+(?: \d+)+)')
|
||||
|
||||
RE_POLY_CONT = re.compile(r'^\s+(?P<coords>\d+(?: \d+)+)')
|
||||
|
||||
RE_MOTION = re.compile(r'''
|
||||
^ TIME
|
||||
\.\.\. MOT
|
||||
|
@ -58,37 +60,30 @@ TIMEZONES = {
|
|||
|
||||
def parse_lon(text: str):
|
||||
size = len(text)
|
||||
return 0 - float(text[0:size-2]) + (float(text[size-2:size]) / 100)
|
||||
|
||||
return 0 - float(text[0:size-2] + '.' + text[size-2:size])
|
||||
|
||||
def parse_lat(text: str):
|
||||
size = len(text)
|
||||
return float(text[0:size-2]) + (float(text[size-2:size]) / 100)
|
||||
return float(text[0:size-2] + '.' + text[size-2:size])
|
||||
|
||||
def parse_location(lon: str, lat: str):
|
||||
return shapely.Point(parse_lon(lon), parse_lat(lat))
|
||||
|
||||
def parse_poly(text: str):
|
||||
points = list()
|
||||
coords = text.split(' ')
|
||||
def parse_poly_coords(text: str) -> list:
|
||||
coords = list()
|
||||
items = text.split(' ')
|
||||
|
||||
for i in range(0, len(coords), 2):
|
||||
lat = coords[i]
|
||||
lon = coords[i+1]
|
||||
for i in range(0, len(items), 2):
|
||||
lat = items[i]
|
||||
lon = items[i+1]
|
||||
|
||||
points.append([parse_lon(lon), parse_lat(lat)])
|
||||
coords.append([parse_lon(lon), parse_lat(lat)])
|
||||
|
||||
points.append([parse_lon(coords[1]), parse_lat(coords[0])])
|
||||
return coords
|
||||
|
||||
return shapely.Polygon(points)
|
||||
|
||||
class AFOSMessageParserState(enum.Enum):
|
||||
NONE = 0
|
||||
SERIAL = enum.auto()
|
||||
ISSUANCE = enum.auto()
|
||||
PRODUCT = enum.auto()
|
||||
BODY = enum.auto()
|
||||
TAGS = enum.auto()
|
||||
FOOTER = enum.auto()
|
||||
def poly_from_coords(coords: list) -> shapely.Polygon:
|
||||
return shapely.Polygon([*coords, [coords[0][0], coords[0][1]]])
|
||||
|
||||
class AFOSMessage(DatabaseTable):
|
||||
__table__ = 'nexrad_afos_message'
|
||||
|
@ -171,14 +166,32 @@ class AFOSMessage(DatabaseTable):
|
|||
def is_warning(self):
|
||||
return self.sig is not None and self.sig == 'W'
|
||||
|
||||
class AFOSMessageParserState(enum.Enum):
|
||||
NONE = 0
|
||||
SERIAL = enum.auto()
|
||||
ISSUANCE = enum.auto()
|
||||
PRODUCT = enum.auto()
|
||||
BODY = enum.auto()
|
||||
TAGS = enum.auto()
|
||||
TAGS_POLY = enum.auto()
|
||||
FOOTER = enum.auto()
|
||||
|
||||
class AFOSMessageParser():
|
||||
__slots__ = 'message', 'state', 'issuance', 'timestamp',
|
||||
__slots__ = (
|
||||
'message', 'state', 'issuance', 'timestamp', 'poly_coords'
|
||||
)
|
||||
|
||||
message: AFOSMessage
|
||||
state: AFOSMessageParserState
|
||||
timestamp: datetime.datetime
|
||||
poly_coords: list
|
||||
|
||||
def __init__(self):
|
||||
self.message = None
|
||||
self.state = None
|
||||
self.issuance = None
|
||||
self.timestamp = None
|
||||
self.poly_coords = None
|
||||
|
||||
def parse_vtec(self, line: str):
|
||||
vtec = VTECEvent.parse(line)
|
||||
|
@ -241,14 +254,25 @@ class AFOSMessageParser():
|
|||
|
||||
def parse_tags(self, line: str):
|
||||
if line == '$$':
|
||||
if len(self.poly_coords) > 0:
|
||||
self.message.poly = shapely.Polygon(self.poly_coords)
|
||||
|
||||
self.state = AFOSMessageParserState.FOOTER
|
||||
return
|
||||
|
||||
#
|
||||
# Parsing for "LAT...LON"
|
||||
#
|
||||
match = RE_POLY.match(line)
|
||||
|
||||
if match is not None:
|
||||
self.message.poly = parse_poly(match['coords'])
|
||||
self.poly_coords.extend(parse_poly_coords(match['coords']))
|
||||
self.state = AFOSMessageParserState.TAGS_POLY
|
||||
return
|
||||
|
||||
#
|
||||
# Parsing for "TIME...MOT...LOC"
|
||||
#
|
||||
match = RE_MOTION.match(line)
|
||||
|
||||
if match is not None:
|
||||
|
@ -256,6 +280,15 @@ class AFOSMessageParser():
|
|||
self.message.speed = int(match['speed'])
|
||||
self.message.location = parse_location(match['lon'], match['lat'])
|
||||
|
||||
def parse_tags_poly(self, line: str):
|
||||
match = RE_POLY_CONT.match(line)
|
||||
|
||||
if match is None:
|
||||
self.state = AFOSMessageParserState.TAGS
|
||||
return self.parse_tags(line)
|
||||
|
||||
self.poly_coords.extend(parse_poly_coords(match['coords']))
|
||||
|
||||
def parse_footer(self, line: str):
|
||||
self.message.forecaster = line
|
||||
|
||||
|
@ -280,6 +313,8 @@ class AFOSMessageParser():
|
|||
self.parse_body(line)
|
||||
elif self.state == AFOSMessageParserState.TAGS:
|
||||
self.parse_tags(line)
|
||||
elif self.state == AFOSMessageParserState.TAGS_POLY:
|
||||
self.parse_tags_poly(line)
|
||||
elif self.state == AFOSMessageParserState.FOOTER:
|
||||
self.parse_footer(line)
|
||||
|
||||
|
@ -302,6 +337,9 @@ class AFOSMessageParser():
|
|||
|
||||
def parse(self, text: str):
|
||||
self.message = AFOSMessage()
|
||||
self.message.text_raw = text
|
||||
|
||||
self.poly_coords = list()
|
||||
self.state = AFOSMessageParserState.SERIAL
|
||||
self.issuance = None
|
||||
self.timestamp = None
|
||||
|
|
Loading…
Add table
Reference in a new issue