@@ -2,62 +2,18 @@ import {
22 stations ,
33 near ,
44 nearest ,
5- type Station ,
65 type NearOptions ,
76 type NearestOptions ,
87} from "@neaps/tide-database" ;
98import {
10- createTidePredictor ,
11- type ExtremesInput ,
12- type TimelineInput ,
13- type Extreme ,
14- type TimelinePoint ,
9+ useStation ,
10+ type Station ,
11+ type StationPredictor ,
12+ type StationExtremesOptions ,
13+ type StationTimelineOptions ,
14+ type StationWaterLevelOptions ,
1515} from "@neaps/tide-predictor" ;
1616
17- type Units = "meters" | "feet" ;
18- type PredictionOptions = {
19- /** Datum to return predictions in. Defaults to the nearest station's datum. */
20- datum ?: string ;
21-
22- /** Units for returned water levels. Defaults to 'meters'. */
23- units ?: Units ;
24-
25- /** Nodal correction fundamentals. Defaults to 'iho'. */
26- nodeCorrections ?: "iho" | "schureman" ;
27- } ;
28-
29- export type ExtremesOptions = ExtremesInput & PredictionOptions ;
30- export type TimelineOptions = TimelineInput & PredictionOptions ;
31- export type WaterLevelOptions = { time : Date } & PredictionOptions ;
32-
33- export type StationPrediction = {
34- datum : string | undefined ;
35- units : Units ;
36- station : Station ;
37- distance ?: number ;
38- } ;
39-
40- export type StationExtremesPrediction = StationPrediction & {
41- extremes : Extreme [ ] ;
42- } ;
43-
44- export type StationTimelinePrediction = StationPrediction & {
45- timeline : TimelinePoint [ ] ;
46- } ;
47-
48- export type StationWaterLevelPrediction = StationPrediction & TimelinePoint ;
49-
50- export type StationPredictor = Station & {
51- distance ?: number ;
52- defaultDatum ?: string ;
53- getExtremesPrediction : ( options : ExtremesOptions ) => StationExtremesPrediction ;
54- getTimelinePrediction : ( options : TimelineOptions ) => StationTimelinePrediction ;
55- getWaterLevelAtTime : ( options : WaterLevelOptions ) => StationWaterLevelPrediction ;
56- } ;
57-
58- const feetPerMeter = 3.2808399 ;
59- const defaultUnits : Units = "meters" ;
60-
6117/**
6218 * Get extremes prediction using the nearest station to the given position.
6319 *
@@ -73,21 +29,21 @@ const defaultUnits: Units = "meters";
7329 * datum: 'MLLW', // optional, defaults to station's datum
7430 * })
7531 */
76- export function getExtremesPrediction ( options : NearestOptions & ExtremesOptions ) {
32+ export function getExtremesPrediction ( options : NearestOptions & StationExtremesOptions ) {
7733 return nearestStation ( options ) . getExtremesPrediction ( options ) ;
7834}
7935
8036/**
8137 * Get timeline prediction using the nearest station to the given position.
8238 */
83- export function getTimelinePrediction ( options : NearestOptions & TimelineOptions ) {
39+ export function getTimelinePrediction ( options : NearestOptions & StationTimelineOptions ) {
8440 return nearestStation ( options ) . getTimelinePrediction ( options ) ;
8541}
8642
8743/**
8844 * Get water level at a specific time using the nearest station to the given position.
8945 */
90- export function getWaterLevelAtTime ( options : NearestOptions & WaterLevelOptions ) {
46+ export function getWaterLevelAtTime ( options : NearestOptions & StationWaterLevelOptions ) {
9147 return nearestStation ( options ) . getWaterLevelAtTime ( options ) ;
9248}
9349
@@ -97,21 +53,21 @@ export function getWaterLevelAtTime(options: NearestOptions & WaterLevelOptions)
9753export function nearestStation ( options : NearestOptions ) {
9854 const data = nearest ( options ) ;
9955 if ( ! data ) throw new Error ( `No stations found with options: ${ JSON . stringify ( options ) } ` ) ;
100- return useStation ( ...data ) ;
56+ return useStation ( ...data , findStation ) ;
10157}
10258
10359/**
10460 * Find stations near the given position.
10561 * @param limit Maximum number of stations to return (default: 10)
10662 */
10763export function stationsNear ( options : NearOptions ) {
108- return near ( options ) . map ( ( [ station , distance ] ) => useStation ( station , distance ) ) ;
64+ return near ( options ) . map ( ( [ station , distance ] ) => useStation ( station , distance , findStation ) ) ;
10965}
11066
11167/**
11268 * Find a specific station by its ID or source ID.
11369 */
114- export function findStation ( query : string ) {
70+ export function findStation ( query : string ) : StationPredictor {
11571 const searches = [ ( s : Station ) => s . id === query , ( s : Station ) => s . source . id === query ] ;
11672
11773 let found : Station | undefined = undefined ;
@@ -122,100 +78,5 @@ export function findStation(query: string) {
12278 }
12379
12480 if ( ! found ) throw new Error ( `Station not found: ${ query } ` ) ;
125-
126- return useStation ( found ) ;
127- }
128-
129- export function useStation ( station : Station , distance ?: number ) : StationPredictor {
130- // If subordinate station, use the reference station for datums and constituents
131- let reference = station ;
132- if ( station . type === "subordinate" && station . offsets ?. reference ) {
133- reference = findStation ( station . offsets ?. reference ) ;
134- }
135- const { datums, harmonic_constituents } = reference ;
136-
137- // Use station chart datum as the default datum if available
138- const defaultDatum = station . chart_datum in datums ? station . chart_datum : undefined ;
139-
140- function getPredictor ( { datum = defaultDatum , nodeCorrections } : PredictionOptions = { } ) {
141- let offset = 0 ;
142-
143- if ( datum ) {
144- const datumOffset = datums ?. [ datum ] ;
145- const mslOffset = datums ?. [ "MSL" ] ;
146-
147- if ( typeof datumOffset !== "number" ) {
148- throw new Error (
149- `Station ${ station . id } missing ${ datum } datum. Available datums: ${ Object . keys ( datums ) . join ( ", " ) } ` ,
150- ) ;
151- }
152-
153- if ( typeof mslOffset !== "number" ) {
154- throw new Error (
155- `Station ${ station . id } missing MSL datum, so predictions can't be given in ${ datum } .` ,
156- ) ;
157- }
158-
159- offset = mslOffset - datumOffset ;
160- }
161-
162- return createTidePredictor ( harmonic_constituents , { offset, nodeCorrections } ) ;
163- }
164-
165- return {
166- ...station ,
167- distance,
168- datums,
169- harmonic_constituents,
170- defaultDatum,
171- getExtremesPrediction ( {
172- datum = defaultDatum ,
173- units = defaultUnits ,
174- nodeCorrections,
175- ...options
176- } : ExtremesOptions ) {
177- const extremes = getPredictor ( { datum, nodeCorrections } )
178- . getExtremesPrediction ( { ...options , offsets : station . offsets } )
179- . map ( ( e ) => toPreferredUnits ( e , units ) ) ;
180-
181- return { datum, units, station, distance, extremes } ;
182- } ,
183-
184- getTimelinePrediction ( {
185- datum = defaultDatum ,
186- units = defaultUnits ,
187- nodeCorrections,
188- ...options
189- } : TimelineOptions ) {
190- const timeline = getPredictor ( { datum, nodeCorrections } )
191- . getTimelinePrediction ( { ...options , offsets : station . offsets } )
192- . map ( ( e ) => toPreferredUnits ( e , units ) ) ;
193-
194- return { datum, units, station, distance, timeline } ;
195- } ,
196-
197- getWaterLevelAtTime ( {
198- time,
199- datum = defaultDatum ,
200- units = defaultUnits ,
201- nodeCorrections,
202- } : WaterLevelOptions ) {
203- const prediction = toPreferredUnits (
204- getPredictor ( { datum, nodeCorrections } ) . getWaterLevelAtTime ( {
205- time,
206- offsets : station . offsets ,
207- } ) ,
208- units ,
209- ) ;
210-
211- return { datum, units, station, distance, ...prediction } ;
212- } ,
213- } ;
214- }
215-
216- function toPreferredUnits < T extends { level : number } > ( prediction : T , units : Units ) : T {
217- let { level } = prediction ;
218- if ( units === "feet" ) level *= feetPerMeter ;
219- else if ( units !== "meters" ) throw new Error ( `Unsupported units: ${ units } ` ) ;
220- return { ...prediction , level } ;
81+ return useStation ( found , undefined , findStation ) ;
22182}
0 commit comments