Skip to content

Commit

Permalink
Merge pull request #979 from akrherz/gh978_tsunami_channels
Browse files Browse the repository at this point in the history
Tsunami Warning channels
  • Loading branch information
akrherz authored Dec 6, 2024
2 parents b120443 + 37086f7 commit 0227e06
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 153 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ci:
autoupdate_schedule: quarterly
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.8.1"
rev: "v0.8.2"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to this library are documented in this file.
- Add `allowed_as_list` option to `iemapp()` helper to stop lists.
- Add maximum risk threshold within SPC outlook message (#969).
- Add support for plotting by FEMA Regions.
- Assign base WFO jabber channel to Tsunami Warnings (#978).
- Include simple table of un-plotted states for `MapPlot(sector="nws")` #967.
- Introduce `radar_ptype` color ramp and `draw_radar_ptype_legend` for
generating plots of HRRR ptype.
Expand All @@ -26,6 +27,7 @@ generating plots of HRRR ptype.

- Accomodate ancient LSRs using `TRACE` as the magnitude field.
- Ensure geometries going into masking helper are CCW, to mask outside of.
- Improve dev experience using `ugc.UGCProvider` (#980).
- Properly check USDM service response prior to parsing it.
- Refine which `MWW` products are not defaulted into the main WFO Channels,
those being VTEC codes `SC`, `MF`, and `GL`.
Expand Down
48 changes: 48 additions & 0 deletions data/product_examples/TSU/TSUWCA_2024.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
521
WEPA41 PAAQ 051849
TSUWCA

BULLETIN
Tsunami Message Number 1
NWS National Tsunami Warning Center Palmer AK
1049 AM PST Thu Dec 5 2024

PZZ530-531-CAZ006-505-506-508-509-109-104-103-101-ORZ021-022-
051949-
/O.NEW.PAAQ.TS.W.0022.241205T1849Z-000000T0000Z/
Coastal areas between and including Davenport, California
(10 miles NW of Santa Cruz) to Douglas/Lane Line, Oregon
(10 miles SW of Florence)
1049 AM PST Thu Dec 5 2024

...A TSUNAMI WARNING IS NOW IN EFFECT WHICH INCLUDES THE
COASTAL AREAS OF CALIFORNIA AND OREGON FROM DAVENPORT
CALIFORNIA, WHICH IS LOCATED 10 MILES NW OF SANTA CRUZ TO
DOUGLAS/LANE LINE OREGON, WHICH IS LOCATED 10 MILES SW OF
FLORENCE...

If you are located in this coastal area, move inland
to higher ground.

Tsunami warnings mean that a tsunami with significant
inundation is possible or is already occurring. Tsunamis are
a series of waves dangerous many hours after initial arrival
time. The first wave may not be the largest.

At 1044 AM Pacific Standard Time on December 5 an earthquake with
preliminary magnitude 7.3 occurred 45 miles southwest of
Eureka California.

Estimated tsunami start times for selected sites are;

Fort Bragg California 1110 AM. PST. December 5.
Crescent City California 1120 AM. PST. December 5.
Port Orford Oregon 1120 AM. PST. December 5.
Brookings Oregon 1125 AM. PST. December 5.
Charleston Oregon 1140 AM. PST. December 5.
San Francisco California 1210 PM. PST. December 5.

The tsunami warning will remain in effect until further notice.
Refer to the internet site tsunami.gov for more information.

$$
20 changes: 10 additions & 10 deletions src/pyiem/nws/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
from collections import OrderedDict
from datetime import datetime, timedelta, timezone
from typing import Optional
from typing import Optional, Union
from zoneinfo import ZoneInfo

from shapely.geometry import MultiPolygon, Polygon
Expand Down Expand Up @@ -264,7 +264,7 @@ def qc_is_emergency(seg):
class TextProductSegment:
"""A segment of a Text Product"""

def __init__(self, text, tp):
def __init__(self, text, tp: "TextProduct"):
"""Constructor"""
self.unixtext = text
self.tp = tp # Reference to parent
Expand Down Expand Up @@ -627,16 +627,14 @@ def parse_headlines(self):
]
return headlines

def get_affected_wfos(self):
def get_affected_wfos(self) -> list[str]:
"""Based on the ugc_provider, figure out which WFOs are impacted by
this product segment"""
affected_wfos = []
for _ugc in self.ugcs:
for wfo in _ugc.wfos:
if wfo not in affected_wfos:
affected_wfos.append(wfo)
affected_wfos.extend(_ugc.wfos)

return affected_wfos
return list(set(affected_wfos))


class TextProduct(WMOProduct):
Expand All @@ -646,22 +644,24 @@ def __init__(
self,
text,
utcnow=None,
ugc_provider=None,
ugc_provider: Optional[Union[ugc.UGCProvider, dict]] = None,
nwsli_provider=None,
parse_segments=True,
):
"""
Constructor
@param text string single text product
@param utcnow used to compute offsets for when this product my be valid
@param ugc_provider a dictionary of UGC objects already setup
@param ugc_provider dict or UGCProvider instance
@param parse_segments should the segments be parsed as well? True
"""
super().__init__(text, utcnow=utcnow)
# NB: Don't use text as it could have been munged by this point
self.afos = None
if isinstance(ugc_provider, dict):
ugc_provider = ugc.UGCProvider(legacy_dict=ugc_provider)
if ugc_provider is None:
ugc_provider = {}
ugc_provider = ugc.UGCProvider()
if nwsli_provider is None:
nwsli_provider = {}
self.ugc_provider = ugc_provider
Expand Down
15 changes: 12 additions & 3 deletions src/pyiem/nws/products/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

from __future__ import absolute_import

from typing import Optional, Union

def parser(text, utcnow=None, ugc_provider=None, nwsli_provider=None):
from pyiem.nws.ugc import UGCProvider


def parser(
text,
utcnow=None,
ugc_provider: Optional[Union[UGCProvider, dict]] = None,
nwsli_provider=None,
):
"""Omnibus parser of NWS Text Data
This is intended to be a catch-all parser of text data. As it currently
Expand All @@ -17,8 +26,8 @@ def parser(text, utcnow=None, ugc_provider=None, nwsli_provider=None):
for when ingesting old data. Many times, the product does not contain
enough information to assign a current valid timestamp to it. So we
need to know the current timestamp to do the relative computation.
ugc_provider (dict, optional): Provides NWS UGC metadata, the dictionary
keys are UGC codes.
ugc_provider (UGCProvider, optional): Provides UGC information for
product parsing.
nwsli_provider (dict, optional): Provides NWS Location Identifiers to
allow lookup of geographic information for station identifiers.
Expand Down
9 changes: 8 additions & 1 deletion src/pyiem/nws/products/_vtec_jabber.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import pandas as pd

# Local
from pyiem.nws.product import TextProduct
from pyiem.nws.ugc import ugcs_to_text
from pyiem.nws.vtec import VTEC, get_action_string
from pyiem.reference import TWEET_CHARS


def build_channels(prod, segment, vtec: VTEC) -> list:
def build_channels(prod: TextProduct, segment, vtec: VTEC) -> list:
"""Build a list of channels for the given segment/vtec."""
ps = f"{vtec.phenomena}.{vtec.significance}"
channels = []
Expand All @@ -31,6 +32,12 @@ def build_channels(prod, segment, vtec: VTEC) -> list:
channels.append(prod.afos)
channels.append(f"{prod.afos[:3]}...")
channels.append(f"{ps}.{vtec.office}{suffix}")
# Tsunami Warning, Watch is a special case
if vtec.phenomena == "TS":
for ugc in segment.ugcs:
for wfo in prod.ugc_provider[ugc].wfos:
if wfo not in channels:
channels.append(wfo)
for ugc in segment.ugcs:
# per state channels
candidate = f"{ps}.{ugc.state}{suffix}"
Expand Down
Loading

0 comments on commit 0227e06

Please sign in to comment.