Implement SPCOutlookMap class

This commit is contained in:
XANTRONIX 2025-03-25 14:41:20 -04:00
parent 5484fb3bb8
commit 8755d4d899

View file

@ -2,9 +2,11 @@ import re
import enum import enum
import shapely import shapely
import datetime import datetime
import cairo
from xmet.db import DatabaseTable from xmet.db import DatabaseTable
from xmet.coord import COORD_SYSTEM from xmet.coord import COORD_SYSTEM
from xmet.map import EquirectMap, MAP_SCREEN_DIMENSIONS, MAP_BOUNDS
from xmet.afos import MONTHS, TIMEZONES from xmet.afos import MONTHS, TIMEZONES
from pyiem.nws.products._outlook_util import ( from pyiem.nws.products._outlook_util import (
@ -114,7 +116,7 @@ def each_poly(parts: list[str]):
polygons, interiors, linestrings = convert_segments(segments) polygons, interiors, linestrings = convert_segments(segments)
# #
# we do our winding logic now # We do our winding logic now
# #
polygons.extend(winding_logic(linestrings)) polygons.extend(winding_logic(linestrings))
@ -123,7 +125,9 @@ def each_poly(parts: list[str]):
# #
for interior in interiors: for interior in interiors:
for i, polygon in enumerate(polygons): for i, polygon in enumerate(polygons):
if not polygon.intersection(interior).is_empty: if polygon.intersection(interior).is_empty:
continue
current = list(polygon.interiors) current = list(polygon.interiors)
current.append(interior) current.append(interior)
polygons[i] = shapely.Polygon(polygon.exterior, current) polygons[i] = shapely.Polygon(polygon.exterior, current)
@ -165,6 +169,9 @@ class SPCOutlookArea(DatabaseTable):
self.outlook_id = None self.outlook_id = None
self.poly = None self.poly = None
def sort_value(self):
raise NotImplementedError
class SPCOutlookProbabilityArea(SPCOutlookArea): class SPCOutlookProbabilityArea(SPCOutlookArea):
__slots__ = ( __slots__ = (
'hazard', 'probability', 'sig', 'hazard', 'probability', 'sig',
@ -177,6 +184,12 @@ class SPCOutlookProbabilityArea(SPCOutlookArea):
'id', 'outlook_id', 'hazard', 'probability', 'sig', 'poly' 'id', 'outlook_id', 'hazard', 'probability', 'sig', 'poly'
) )
def sort_value(self):
if self.probability == 'SIGN':
return 1.0
return float(self.probability)
class SPCOutlookCategoryArea(SPCOutlookArea): class SPCOutlookCategoryArea(SPCOutlookArea):
__slots__ = ( __slots__ = (
'category' 'category'
@ -189,6 +202,18 @@ class SPCOutlookCategoryArea(SPCOutlookArea):
'id', 'outlook_id', 'category', 'poly' 'id', 'outlook_id', 'category', 'poly'
) )
__category_levels__ = {
'TSTM': 0,
'MRGL': 1,
'SLGT': 2,
'ENH': 3,
'MDT': 4,
'HIGH': 5
}
def sort_value(self):
return self.__category_levels__[self.category]
class SPCOutlook(): class SPCOutlook():
__slots__ = ( __slots__ = (
'id', 'timestamp_issued', 'timestamp_start', 'timestamp_end', 'day', 'id', 'timestamp_issued', 'timestamp_start', 'timestamp_end', 'day',
@ -216,6 +241,12 @@ class SPCOutlook():
self.probabilities = list() self.probabilities = list()
self.categories = list() self.categories = list()
def sorted_probabilities(self) -> list[SPCOutlookProbabilityArea]:
return sorted(self.probabilities, key=lambda p: p.sort_value())
def sorted_categories(self) -> list[SPCOutlookCategoryArea]:
return sorted(self.categories, key=lambda p: p.sort_value())
class SPCOutlookParserState(enum.Enum): class SPCOutlookParserState(enum.Enum):
HEADER = 1 HEADER = 1
OFFICE = enum.auto() OFFICE = enum.auto()
@ -455,3 +486,66 @@ class SPCOutlookParser():
self.parse_body(line) self.parse_body(line)
return self.outlook return self.outlook
class SPCOutlookMap(EquirectMap):
TEXT_FONT = 'Muli'
LOGO_RATIO = 75.0 / 275.0
LOGO_WIDTH = 360
LOGO_HEIGHT = LOGO_RATIO * LOGO_WIDTH
LOGO_MARGIN = 16
__category_colors__ = {
'TSTM': (212, 240, 213),
'MRGL': ( 80, 201, 134),
'SLGT': (255, 255, 81),
'ENH': (255, 192, 108),
'MDT': (255, 80, 80),
'HIGH': (255, 80, 255)
}
def __init__(self):
super().__init__(*MAP_SCREEN_DIMENSIONS, MAP_BOUNDS)
def draw_logo(self, cr: cairo.Context, path: str):
width = self.LOGO_WIDTH
height = self.LOGO_HEIGHT
margin = self.LOGO_MARGIN
x = margin
y = self.height - height - margin
self.draw_from_file(cr, path, x, y, width, height)
def draw_annotation(self, cr: cairo.Context, text: str):
cr.select_font_face('Muli')
cr.set_font_size(28)
extents = cr.text_extents(text)
cr.move_to(self.width - extents.width - 48,
self.height - extents.height - 16)
cr.show_text(text)
def draw_categories(self,
cr: cairo.Context,
outlook: SPCOutlook):
cr.save()
for category in outlook.sorted_categories():
color = self.__category_colors__[category.category]
r = color[0] / 255.0
g = color[1] / 255.0
b = color[2] / 255.0
cr.set_source_rgba(r, g, b, 0.35)
self.draw_polygon(cr, category.poly)
cr.fill()
cr.set_source_rgba(r*0.75, g*0.75, b*0.75, 0.8)
self.draw_polygon(cr, category.poly)
cr.stroke()
cr.restore()