buoydata
v0.2.2
Published
Modern TypeScript SDK for NDBC realtime buoy data.
Readme
buoydata
https://www.npmjs.com/package/buoydata
Modern TypeScript SDK for NOAA NDBC realtime buoy data. The library provides a fetch layer, parsing helpers, and typed models for standard meteorological measurements while still supporting arbitrary realtime2 data types.
Reminder: No support or warranty is provided or expected. Use at your own risk.
Installation
pnpm add buoydataQuickstart
import {
fetchRealtimeData,
parseRealtimeData,
parseRealtimeTable,
} from 'buoydata';
const raw = await fetchRealtimeData({ buoyId: '46026', type: 'txt' });
const data = parseRealtimeData('46026', raw);
console.log(data.measurements[0].wind.averageSpeed);
const table = parseRealtimeTable(raw);
console.log(table.headers);Requirements
- Node.js 18+ (server-side only; this package depends on
nodejs-polars).
Node usage
Node 18+ includes fetch globally. If you need a custom client, pass it explicitly:
import { fetchRealtimeData } from 'buoydata';
import fetch from 'node-fetch';
const raw = await fetchRealtimeData({ buoyId: '46026', fetch });API
fetchRealtimeData
Fetches realtime2 files from NDBC.
fetchRealtimeData({
buoyId: string,
type?: string,
fetch?: typeof fetch,
requestInit?: RequestInit,
baseUrl?: string,
}): Promise<string>buildRealtimeUrl
Builds the realtime2 URL for a buoy and file type.
buildRealtimeUrl(buoyId: string, type?: string, baseUrl?: string): stringfetchBuoyList and fetchStationIndex
Retrieve station IDs using the NDBC active station XML feed (no longer 404-prone). You can optionally include inactive stations from the station catalog, and fetchStationIndex gives you an isActive helper without needing to reparse data.
const active = await fetchBuoyList(); // active IDs only
const all = await fetchBuoyList({ includeInactive: true }); // full catalog
const index = await fetchStationIndex();
index.isActive('46026'); // true/falseparseRealtimeData
Parses a realtime2 text file into typed Measurement objects. Standard fields are mapped into structured measurement fields. Unknown columns are ignored unless includeUnknownFields is enabled.
parseRealtimeData(
buoyId: string,
rawText: string,
options?: {
coerceNumbers?: boolean;
missingValue?: number | null;
missingTokens?: string[];
commentPrefix?: string;
includeUnknownFields?: boolean;
},
): BuoyDataparseRealtimeTable
Parses a realtime2 text file into a generic table representation with headers, units, and raw rows.
parseRealtimeTable(
rawText: string,
options?: {
coerceNumbers?: boolean;
missingValue?: number | null;
missingTokens?: string[];
commentPrefix?: string;
},
): RealtimeTableparseRealtimeTableFrame
Parses a realtime2 text file into a Polars DataFrame with headers, units, and raw rows.
parseRealtimeTableFrame(
rawText: string,
options?: {
coerceNumbers?: boolean;
missingValue?: number | null;
missingTokens?: string[];
commentPrefix?: string;
},
): RealtimeTableFrametoDataFrame
Converts a RealtimeTable into a Polars DataFrame.
toDataFrame(table: RealtimeTable): DataFrameparseRow
Parses a single row into values using whitespace splitting and missing-data handling.
parseRow(
rawRow: string,
options?: {
coerceNumbers?: boolean;
missingValue?: number | null;
missingTokens?: string[];
},
): ParsedValue[]objectifyTable
Converts a RealtimeTable into an array of records keyed by header values.
objectifyTable(table: RealtimeTable): RealtimeRecord[]getMeasurementDate
Creates a UTC Date instance from a Measurement (using year, month, day, hour, minute).
getMeasurementDate(measurement: Measurement): DateURL utilities
formatQueryParams(params: QueryParams): string
buildURL(base: string, path?: string, params?: QueryParams): stringData models
Measurement
Structured representation of standard meteorological data:
year,month,day,hour,minuteairTemperature,dewpointTemperaturepressureTendancy,seaLevelPressure,stationVisibilitywind(direction,averageSpeed,peakGustSpeed)water(averagePeriod,dominantDirection,dominantPeriod,significantHeight,surfaceTemperature,tide)
BuoyData
{
id: string;
measurements: Measurement[];
}RealtimeTable
{
headers: string[];
units: string[];
rows: (string | number | null)[][];
rawRows: string[];
}RealtimeTableFrame
{
headers: string[];
units: string[];
frame: DataFrame;
rawRows: string[];
}RealtimeRecord
Record<string, string | number | null>Parsing behavior
- Comment lines start with
#and are ignored for table parsing. - The units row (typically
#yr mo dy ...) is parsed intounits. - Missing data tokens default to
MMand numeric 9s (e.g.99,999,9999,99.0). parseRealtimeTableusesnullas the default missing value;parseRealtimeDatausesNaNby default to align with numeric measurement fields.- Numbers are coerced automatically unless
coerceNumbersis set tofalse.
Code layout
src/
index.ts Public exports
models/
measurement.ts Typed data models for standard met data
table.ts Generic table and record types
realtime/
fetch.ts Fetch layer and realtime URL builder
parser.ts Table parsing, objectification, and measurement mapping
utils/
date.ts Measurement date helper
url.ts URL and query param utilities
tests/
fixtures/ Downloaded realtime2 sample files
parser.test.ts Parsing tests across formats
fetch.test.ts Fetch layer tests (mocked)
url.test.ts URL/query param tests
date.test.ts Measurement date utility testArchitecture diagrams
High-level flow
+----------------------+
| NDBC realtime2 |
| (https endpoint) |
+----------+-----------+
|
| fetchRealtimeData
v
+------+------+
| rawText |
+------+------+
|
+---------------+----------------+
| |
v v
parseRealtimeTable parseRealtimeData
| |
v v
RealtimeTable BuoyData (typed)
|
v
objectifyTable
|
v
RealtimeRecord[]Parsing pipeline (parseRealtimeTable)
rawText
|
v
normalizeLines (trim, drop blanks)
|
v
filter comment lines ("# ")
|
v
parse header row --> headers[]
|
v
parse units row --> units[]
|
v
parse data rows --> rows[][]Measurement mapping (parseRealtimeData)
RealtimeTable
|
v
objectifyTable -> RealtimeRecord[]
|
v
toMeasurement
|
v
Measurement[] (BuoyData.measurements)Testing
pnpm testLicense
MIT
