Parse National Weather Service (NWS) text products in Elixir.
NwsText turns one NWS text product into plain structs: WMO header, AWIPS ID,
issue times, ordered segments, UGC codes, VTEC, polygons, storm motion, impact
tags, headlines, labeled narrative sections, and embedded CAP 1.2 XML. It is a
pure parser. It does no network, XMPP, database, or location lookups, and it
makes no alerting decisions. The same input and reference time always give the
same result.
This project is not affiliated with or endorsed by NOAA or the National Weather Service.
Installation
def deps do
[{:nws_text, "~> 0.1"}]
endUsage
Complete products
{:ok, product} = NwsText.parse(raw_text, reference_time: ~U[2026-10-05 20:15:00Z])
product.awips_id #=> "SVRHUN"
product.issued_at #=> ~U[2026-10-05 20:10:00Z]
segment = hd(product.segments)
Enum.map(segment.ugc_codes, & &1.raw_code) #=> ["ALC089", "ALC091"]
segment.vtec_codes #=> [%NwsText.Model.VTEC{action: "NEW", phenomenon: "SV", ...}]
segment.polygon #=> %Geo.Polygon{srid: 4326, ...}
segment.impact_tags #=> [%{name: "MAX HAIL SIZE", value: "1.00 IN"}]reference_time is required and must be a UTC DateTime. The parser uses it
to resolve day-only WMO and UGC times to the nearest previous, current, or next
month. It never reads the wall clock.
parse/2 returns {:error, %NwsText.ParseError{}} for an invalid WMO header or
malformed UGC or VTEC coding. Problems with optional fields, such as a bad
polygon or motion line, are reported in product.diagnostics. The field is
then nil or left out. The parser never returns partial geometry.
Narrative text
Some sources deliver only the product body, without the WMO header, UGC, or VTEC
lines. Use parse_narrative/1 for that text:
{:ok, narrative} = NwsText.parse_narrative(body_text)
narrative.headlines #=> ["FLOOD WARNING REMAINS IN EFFECT UNTIL FURTHER NOTICE"]
narrative.sections #=> [%{label: "WHAT", text: "Minor flooding is occurring."}, ...]
narrative.impact_tags #=> []Sections are literal %{label, text} maps in source order. The known labels are
WHAT, WHERE, WHEN, HAZARD, SOURCE, IMPACT (IMPACTS is reported as
IMPACT), ADDITIONAL DETAILS, and PRECAUTIONARY/PREPAREDNESS ACTIONS. Other
text stays in the raw text only.
Behavior notes
- Times: A product time line such as
1011 AM EDT Tue Oct 13 2026setsissued_at. Without one,issued_atis the WMO time. Supported zones are UTC, GMT, and US zones: Atlantic, Eastern, Central, Mountain, Pacific, Alaska, Hawaii, Samoa, and Chamorro (Guam). Like pyIEM,1300 PMmeans 13:00, and UTC or GMT times may omit AM/PM. - Coordinates:
LAT...LONandTIME...MOT...LOChave no hemisphere marker. Longitudes are west, except for products fromPGUM(Guam), which are east. - CAP: Embedded CAP XML is limited to 1 MB and parsed with Saxy. Entities are not expanded, and any DTD or entity declaration is rejected. CAP fields are matched by namespace URI. CAP text is never scanned for UGC or VTEC lines.
- Raw text:
raw_textis kept on the product and on every segment, so nothing is lost when a field is not modeled.
Development
mix deps.get
mix test
mix format --check-formatted
mix credo --strict
Releases are made by maintainers with scripts/release; see
PUBLISHING.md.
The test corpus contains 169 unmodified product examples from pyIEM. Its expected results are generated independently from the raw text, not from the parser. See test/fixtures/README.md for attribution and for how to regenerate them.
License
MIT. See LICENSE. The pyIEM test fixtures are MIT licensed by Iowa
State University; see test/fixtures/LICENSE.pyiem.