Implement SPCOutlookMap class
This commit is contained in:
parent
5484fb3bb8
commit
8755d4d899
1 changed files with 99 additions and 5 deletions
104
lib/xmet/spc.py
104
lib/xmet/spc.py
|
@ -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,10 +125,12 @@ 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:
|
||||||
current = list(polygon.interiors)
|
continue
|
||||||
current.append(interior)
|
|
||||||
polygons[i] = shapely.Polygon(polygon.exterior, current)
|
current = list(polygon.interiors)
|
||||||
|
current.append(interior)
|
||||||
|
polygons[i] = shapely.Polygon(polygon.exterior, current)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Buffer zero any invalid polygons
|
# Buffer zero any invalid polygons
|
||||||
|
@ -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()
|
||||||
|
|
Loading…
Add table
Reference in a new issue