api/ndfdWeather.ts
Purpose
This module fetches, validates, and normalizes hourly weather forecast data from the NOAA NDFD (National Digital Forecast Database) XML API into a strongly typed internal format.
Its responsibilities include:
- Constructing a constrained NDFD time-series request for a single geographic point
- Fetching DWML/XML forecast data with timeout protection
- Parsing XML into structured JSON
- Validating the expected XML structure and parameter layouts
- Aligning weather parameters by timestamp
- Converting units into internal application units
- Filtering out incomplete or invalid forecast rows
- Returning a cacheable weather forecast payload with an expiration boundary
The resulting output is a normalized array of WeatherPoint objects suitable for downstream WBGT or forecast processing.
Invariants
The module enforces several important invariants to guarantee predictable downstream behavior.
Coordinate Validation
lat and lon must always satisfy:
lat ∈ [-90, 90]lon ∈ [-180, 180]- both values must be finite numbers
Violations throw a RangeError.
Positive Integer Inputs
The following arguments must always be positive integers:
numHoursForecasttimeoutMs
Invalid values throw a RangeError.
Parameter Completeness
A forecast point is only emitted when all required parameters exist for the same timestamp:
- temperature
- humidity
- wind speed
- cloud amount
Incomplete timestamps are discarded.
This guarantees every emitted WeatherPoint is structurally complete.
Missing Sentinel Handling
NDFD commonly uses -9999 to represent missing values.
These values are always ignored and never emitted into forecast output.
Unit Normalization
Returned weather values are normalized into internal units:
| Source | Internal |
|---|---|
| Fahrenheit | Celsius |
| Knots | meters/second |
Conversions are rounded to one decimal place.
Time Ordering
All emitted forecast points are guaranteed to be sorted in ascending chronological order.
Cache Validity Contract
validTo is always computed as:
(latest forecast timestamp) + 1 hour
This defines the upper bound of cache validity for the fetched forecast window.
Structural Parse Safety
The parser assumes:
dwml.data.parametersmust exist- required parameter blocks must exist
- referenced
time-layoutkeys must resolve correctly
Malformed XML structures throw descriptive parse errors instead of silently failing.
Variants
XML Shape Variants
NDFD DWML responses are not fully consistent.
This module handles several schema variants:
layout-key
May appear as:
- XML child element
- XML attribute
The parser supports both forms.
Single vs Array Nodes
XML nodes may deserialize as:
- single object/value
- array of objects/values
The toArray() helper normalizes both representations into arrays.
Missing Forecast Rows
Different parameter blocks may contain different timestamp counts.
The module resolves this by intersecting timestamps and only keeping fully populated rows.
Timeout Behavior
Network requests are abortable via AbortController.
If the timeout is exceeded:
- the request is aborted
- fetch rejects
- the caller receives an error
Windowing Variant
The requested forecast range is determined dynamically using:
ndfdLocalWindow(numHoursForecast, windowStart)
This allows:
- arbitrary forecast durations
- arbitrary starting windows
- deterministic local-hour alignment
Exports
WeatherFetchResult
type WeatherFetchResult = {
points: WeatherPoint[];
validTo: number;
};
Represents:
- normalized forecast points
- cache expiration timestamp
fetchNdfdWeatherSeries(...)
async function fetchNdfdWeatherSeries(
lat: number,
lon: number,
numHoursForecast = 24,
timeoutMs = 12000,
windowStart: Date | string = new Date(),
): Promise<WeatherFetchResult>
Fetches and parses hourly NDFD forecast data for a single coordinate.
Parameters
| Parameter | Description |
|---|---|
lat | Latitude |
lon | Longitude |
numHoursForecast | Number of forecast hours requested |
timeoutMs | Network timeout in milliseconds |
windowStart | Forecast window start time |
Returns
A Promise<WeatherFetchResult> containing:
- chronologically ordered forecast points
- cache validity expiration timestamp
Throws
Throws when:
- coordinates are invalid
- arguments are invalid
- fetch fails
- timeout occurs
- XML structure is malformed
- no usable forecast points exist