/** * Utility functions for marine conditions data formatting and interpretation */ import type { GridpointResponse } from '../types/noaa.js'; /** * NOAA Marine Conditions extracted from gridpoint data */ export interface NOAAMarineConditions { waveHeight?: number; wavePeriod?: number; waveDirection?: number; windSpeed?: number; windDirection?: number; windGust?: number; timestamp: string; } /** * Extract marine conditions from NOAA gridpoint response */ export declare function extractNOAAMarineConditions(gridpoint: GridpointResponse): NOAAMarineConditions | null; /** * Format wave height with appropriate units and precision */ export declare function formatWaveHeight(meters: number | undefined): string; /** * Format wave period with units */ export declare function formatWavePeriod(seconds: number | undefined): string; /** * Format wind speed with units (converts km/h to knots for marine) */ export declare function formatWindSpeed(kmh: number | undefined): string; /** * Format ocean current velocity */ export declare function formatCurrentVelocity(metersPerSecond: number | undefined): string; /** * Convert degrees to cardinal/ordinal direction */ export declare function formatDirection(degrees: number | undefined): string; /** * Sea state — one ordered table, and everything that names a sea state derives from it. * * WMO Code Table 3700 (State of the sea; the Douglas sea scale), read 2026-09-01 from * NOAA/NODC's GTSPP transcription and the UK Met Office coast-and-sea glossary, which agree * on every term and bound. The rung names are the code table's terms in its capitalisation. * The thresholds are the code table's bounds and are locked at every seam by * tests/unit/marine-band-rounding.test.ts (v1.25.6) — do not move them. * * Codes 0 (Calm (glassy), 0 m) and 1 (Calm (rippled), 0–0.1 m) share the lowest rung: the * band keys on the one-decimal display value, and `shown < 0.1` means the report prints * `0.0m`, which both codes do. The rung carries the term the two codes share, `Calm`, rather * than claiming either parenthetical for a sea the display cannot tell apart. * * Boundary convention: an exact bound bands into the HIGHER rung (`shown < upperBound`, the * cautious side, as v1.25.6 locked it), whereas the code table's own coding rule assigns an * exact bounding height to the lower code figure. docs/TOOLS.md states this. * * Recommendations belong to the threshold RANGE, not to the name — a rung renamed by this * table keeps the advice its range always carried. */ export declare const SEA_STATE_TIERS: { readonly calm: { readonly marker: '🟢'; readonly blurb: 'Safe for most vessels'; }; readonly moderate: { readonly marker: '🟡'; readonly blurb: 'Challenging for small craft'; }; readonly rough: { readonly marker: '🟠'; readonly blurb: 'Hazardous for small vessels'; }; readonly veryRough: { readonly marker: '🔴'; readonly blurb: 'Dangerous for most vessels'; }; readonly extreme: { readonly marker: '🟣'; readonly blurb: 'Extremely dangerous'; }; }; /** Severity tiers, in declaration order = severity order. */ export type SeaStateTier = keyof typeof SEA_STATE_TIERS; export declare const SEA_STATE_SCALE: readonly [{ readonly wmoCode: '0–1'; readonly name: 'Calm'; readonly upperBound: 0.1; readonly tier: 'calm'; readonly recommendation: 'Ideal for all water activities'; }, { readonly wmoCode: '2'; readonly name: 'Smooth (wavelets)'; readonly upperBound: 0.5; readonly tier: 'calm'; readonly recommendation: 'Excellent conditions for all vessels'; }, { readonly wmoCode: '3'; readonly name: 'Slight'; readonly upperBound: 1.25; readonly tier: 'calm'; readonly recommendation: 'Good conditions for most activities'; }, { readonly wmoCode: '4'; readonly name: 'Moderate'; readonly upperBound: 2.5; readonly tier: 'moderate'; readonly recommendation: 'Safe for experienced boaters'; }, { readonly wmoCode: '5'; readonly name: 'Rough'; readonly upperBound: 4; readonly tier: 'rough'; readonly recommendation: 'Use caution, especially for small craft'; }, { readonly wmoCode: '6'; readonly name: 'Very rough'; readonly upperBound: 6; readonly tier: 'veryRough'; readonly recommendation: 'Hazardous for small vessels, secure all gear'; }, { readonly wmoCode: '7'; readonly name: 'High'; readonly upperBound: 9; readonly tier: 'veryRough'; readonly recommendation: 'Dangerous conditions, avoid non-essential travel'; }, { readonly wmoCode: '8'; readonly name: 'Very high'; readonly upperBound: 14; readonly tier: 'extreme'; readonly recommendation: 'Very dangerous, only experienced vessels should be out'; }, { readonly wmoCode: '9'; readonly name: 'Phenomenal'; readonly upperBound: number; readonly tier: 'extreme'; readonly recommendation: 'Extremely dangerous, all vessels should seek shelter'; }]; /** The rung names, derived from the table — the only vocabulary a sea-state `level` can carry. */ export type SeaStateLevel = (typeof SEA_STATE_SCALE)[number]['name']; /** The level a report carries when it has no wave-height data. Not a severity. */ export declare const NO_DATA_LEVEL: 'Unknown'; /** The marker for `NO_DATA_LEVEL`. Appears in no severity row of the legend. */ export declare const NO_DATA_MARKER = "\u26AA"; /** * The severity marker for a level. Found by name in the table, so the names are never * copied; an unknown name is a thrown error, never a fallback colour. */ export declare function seaStateMarker(level: SeaStateLevel | typeof NO_DATA_LEVEL): string; /** * The legend: one row per severity tier, generated from the table so the marker, the rung * names and the range can never disagree with the header that used them. The ranges are the * true union of each tier's rungs; the top row is open-ended. */ export declare function formatSeaStateLegend(): string; /** * Categorize wave height */ export interface WaveHeightCategory { description: string; level: SeaStateLevel | typeof NO_DATA_LEVEL; recommendation: string; } export declare function getWaveHeightCategory(meters: number | undefined): WaveHeightCategory; /** * Overall safety assessment based on multiple factors */ export interface SafetyAssessment { level: SeaStateLevel | typeof NO_DATA_LEVEL; description: string; recommendation: string; } export declare function getSafetyAssessment(totalWaveHeight: number | undefined, windWaveHeight: number | undefined, swellHeight: number | undefined, wavePeriod: number | undefined): SafetyAssessment; /** * The shared sea-state block: both marine render paths (NOAA gridpoint and Open-Meteo) obtain * their sea-state block from this function; neither formats the header, the sea-state line or * the safety line itself. The header line is byte-compatible with the pre-existing Open-Meteo * render site (`## ${marker} Current Conditions: ${level}`), which * tests/unit/marine-sea-state-taxonomy.test.ts contract 6 parses directly — do not reformat it. */ export declare function formatSeaStateBlock(safety: SafetyAssessment): string; /** * The no-marine-cell note: shown when a location resolves to a point the marine model has no * cell for (most often a place name resolving to a land centroid). Takes a plain flag, not a * `ResolvedLocation` — this module must not import from `locationResolver.ts`, which pulls in * `LocationStore`, `GeocodingService`, `Cache` and `NominatimService`. Must never contain * `NO_DATA_MARKER`: tests/unit/marine-sea-state-taxonomy.test.ts counts that marker and expects * exactly two occurrences in a no-data report. * * The note deliberately does **not** restate the resolved place name or its coordinates. Both * already render above it — `formatLocationLine` prepends `**Location:** (lat, lon)` for * every name-based resolution (`locationResolver.ts:47`), and the report's own `**Location:**` * line carries the coordinates on every path. `fromPlaceName` therefore carries provenance * only: the long variant explains the *mechanism* that produced an inland point without * repeating data the reader has already been shown four lines earlier. */ export declare function formatNoMarineCellNote(opts: { fromPlaceName: boolean; }): string; //# sourceMappingURL=marine.d.ts.map