neaps 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -27,11 +27,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  //#endregion
28
28
  let geolib = require("geolib");
29
29
  let _neaps_tide_database = require("@neaps/tide-database");
30
- _neaps_tide_database = __toESM(_neaps_tide_database);
31
30
  let _neaps_tide_predictor = require("@neaps/tide-predictor");
32
31
  _neaps_tide_predictor = __toESM(_neaps_tide_predictor);
33
32
 
34
33
  //#region src/index.ts
34
+ const feetPerMeter = 3.2808399;
35
+ const defaultUnits = "meters";
35
36
  /**
36
37
  * Get extremes prediction using the nearest station to the given position.
37
38
  *
@@ -73,7 +74,7 @@ function nearestStation(position) {
73
74
  * @param limit Maximum number of stations to return (default: 10)
74
75
  */
75
76
  function stationsNear(position, limit = 10) {
76
- return _neaps_tide_database.default.map((station) => ({
77
+ return _neaps_tide_database.stations.map((station) => ({
77
78
  station,
78
79
  distance: (0, geolib.getDistance)(position, station)
79
80
  })).sort((a, b) => a.distance - b.distance).slice(0, limit).map(({ station, distance }) => useStation(station, distance));
@@ -85,7 +86,7 @@ function findStation(query) {
85
86
  const searches = [(s) => s.id === query, (s) => s.source.id === query];
86
87
  let found = void 0;
87
88
  for (const search of searches) {
88
- found = _neaps_tide_database.default.find(search);
89
+ found = _neaps_tide_database.stations.find(search);
89
90
  if (found) break;
90
91
  }
91
92
  if (!found) throw new Error(`Station not found: ${query}`);
@@ -105,10 +106,7 @@ function useStation(station, distance) {
105
106
  if (typeof mslOffset !== "number") throw new Error(`Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`);
106
107
  offset = mslOffset - datumOffset;
107
108
  }
108
- return (0, _neaps_tide_predictor.default)(harmonic_constituents, {
109
- phaseKey: "phase_UTC",
110
- offset
111
- });
109
+ return (0, _neaps_tide_predictor.default)(harmonic_constituents, { offset });
112
110
  }
113
111
  return {
114
112
  ...station,
@@ -116,35 +114,49 @@ function useStation(station, distance) {
116
114
  datums,
117
115
  harmonic_constituents,
118
116
  defaultDatum,
119
- getExtremesPrediction({ datum = defaultDatum, ...input }) {
117
+ getExtremesPrediction({ datum = defaultDatum, units = defaultUnits, ...options }) {
120
118
  return {
121
119
  datum,
122
- distance,
120
+ units,
123
121
  station,
122
+ distance,
124
123
  extremes: getPredictor({ datum }).getExtremesPrediction({
125
- ...input,
124
+ ...options,
126
125
  offsets: station.offsets
127
- })
126
+ }).map((e) => toPreferredUnits(e, units))
128
127
  };
129
128
  },
130
- getTimelinePrediction({ datum = defaultDatum, ...params }) {
129
+ getTimelinePrediction({ datum = defaultDatum, units = defaultUnits, ...options }) {
131
130
  if (station.type === "subordinate") throw new Error(`Timeline predictions are not supported for subordinate stations.`);
132
131
  return {
133
132
  datum,
133
+ units,
134
134
  station,
135
- timeline: getPredictor({ datum }).getTimelinePrediction(params)
135
+ distance,
136
+ timeline: getPredictor({ datum }).getTimelinePrediction(options).map((e) => toPreferredUnits(e, units))
136
137
  };
137
138
  },
138
- getWaterLevelAtTime({ time, datum = defaultDatum }) {
139
+ getWaterLevelAtTime({ time, datum = defaultDatum, units = defaultUnits }) {
139
140
  if (station.type === "subordinate") throw new Error(`Water level predictions are not supported for subordinate stations.`);
140
141
  return {
141
142
  datum,
143
+ units,
142
144
  station,
143
- ...getPredictor({ datum }).getWaterLevelAtTime({ time })
145
+ distance,
146
+ ...toPreferredUnits(getPredictor({ datum }).getWaterLevelAtTime({ time }), units)
144
147
  };
145
148
  }
146
149
  };
147
150
  }
151
+ function toPreferredUnits(prediction, units) {
152
+ let { level } = prediction;
153
+ if (units === "feet") level *= feetPerMeter;
154
+ else if (units !== "meters") throw new Error(`Unsupported units: ${units}`);
155
+ return {
156
+ ...prediction,
157
+ level
158
+ };
159
+ }
148
160
 
149
161
  //#endregion
150
162
  exports.findStation = findStation;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["stations","found: Station | undefined"],"sources":["../src/index.ts"],"sourcesContent":["import { getDistance } from 'geolib'\nimport stations, { type Station } from '@neaps/tide-database'\nimport tidePredictor, {\n type TimeSpan,\n type ExtremesInput\n} from '@neaps/tide-predictor'\nimport type { GeolibInputCoordinates } from 'geolib/es/types'\n\ntype DatumOption = {\n /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */\n datum?: string\n}\n\nexport type ExtremesOptions = ExtremesInput & DatumOption\nexport type TimelineOptions = TimeSpan & DatumOption\nexport type WaterLevelOptions = { time: Date } & DatumOption\n\n/**\n * Get extremes prediction using the nearest station to the given position.\n *\n * @example\n * ```ts\n * import { getExtremesPrediction } from 'neaps'\n *\n * const prediction = getExtremesPrediction({\n * latitude: 26.7, // or `lat`\n * longitude: -80.05, // or `lng` or `lon`\n * start: new Date('2025-12-17'),\n * end: new Date('2025-12-18'),\n * datum: 'MLLW', // optional, defaults to MLLW if available\n * })\n */\nexport function getExtremesPrediction(\n options: GeolibInputCoordinates & ExtremesOptions\n) {\n return nearestStation(options).getExtremesPrediction(options)\n}\n\n/**\n * Get timeline prediction using the nearest station to the given position.\n */\nexport function getTimelinePrediction(\n options: GeolibInputCoordinates & TimelineOptions\n) {\n return nearestStation(options).getTimelinePrediction(options)\n}\n\n/**\n * Get water level at a specific time using the nearest station to the given position.\n */\nexport function getWaterLevelAtTime(\n options: GeolibInputCoordinates & WaterLevelOptions\n) {\n return nearestStation(options).getWaterLevelAtTime(options)\n}\n\n/**\n * Find the nearest station to the given position.\n */\nexport function nearestStation(position: GeolibInputCoordinates) {\n return stationsNear(position, 1)[0]\n}\n\n/**\n * Find stations near the given position.\n * @param limit Maximum number of stations to return (default: 10)\n */\nexport function stationsNear(position: GeolibInputCoordinates, limit = 10) {\n return stations\n .map((station) => ({ station, distance: getDistance(position, station) }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, limit)\n .map(({ station, distance }) => useStation(station, distance))\n}\n\n/**\n * Find a specific station by its ID or source ID.\n */\nexport function findStation(query: string) {\n const searches = [\n (s: Station) => s.id === query,\n (s: Station) => s.source.id === query\n ]\n\n let found: Station | undefined = undefined\n\n for (const search of searches) {\n found = stations.find(search)\n if (found) break\n }\n\n if (!found) throw new Error(`Station not found: ${query}`)\n\n return useStation(found)\n}\n\nexport function useStation(station: Station, distance?: number) {\n // If subordinate station, use the reference station for datums and constituents\n let reference = station\n if (station.type === 'subordinate') {\n reference = findStation(station.offsets?.reference || '')\n }\n const { datums, harmonic_constituents } = reference\n\n // Use MLLW as the default datum if available\n const defaultDatum = 'MLLW' in datums ? 'MLLW' : undefined\n\n function getPredictor({ datum = defaultDatum }: DatumOption = {}) {\n let offset = 0\n\n if (datum) {\n const datumOffset = datums?.[datum]\n const mslOffset = datums?.['MSL']\n\n if (typeof datumOffset !== 'number') {\n throw new Error(\n `Station ${station.id} missing ${datum} datum. Available datums: ${Object.keys(datums || {}).join(', ')}`\n )\n }\n\n if (typeof mslOffset !== 'number') {\n throw new Error(\n `Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`\n )\n }\n\n offset = mslOffset - datumOffset\n }\n\n return tidePredictor(harmonic_constituents, {\n phaseKey: 'phase_UTC',\n offset\n })\n }\n\n return {\n ...station,\n distance,\n datums,\n harmonic_constituents,\n defaultDatum,\n getExtremesPrediction({ datum = defaultDatum, ...input }: ExtremesOptions) {\n return {\n datum,\n distance,\n station,\n extremes: getPredictor({ datum }).getExtremesPrediction({\n ...input,\n offsets: station.offsets\n })\n }\n },\n\n getTimelinePrediction({\n datum = defaultDatum,\n ...params\n }: TimelineOptions) {\n if (station.type === 'subordinate') {\n throw new Error(\n `Timeline predictions are not supported for subordinate stations.`\n )\n }\n\n return {\n datum,\n station,\n timeline: getPredictor({ datum }).getTimelinePrediction(params)\n }\n },\n\n getWaterLevelAtTime({ time, datum = defaultDatum }: WaterLevelOptions) {\n if (station.type === 'subordinate') {\n throw new Error(\n `Water level predictions are not supported for subordinate stations.`\n )\n }\n\n return {\n datum,\n station,\n ...getPredictor({ datum }).getWaterLevelAtTime({ time })\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,sBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,sBAAsB,QAAQ;;;;;AAM/D,SAAgB,sBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,sBAAsB,QAAQ;;;;;AAM/D,SAAgB,oBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,oBAAoB,QAAQ;;;;;AAM7D,SAAgB,eAAe,UAAkC;AAC/D,QAAO,aAAa,UAAU,EAAE,CAAC;;;;;;AAOnC,SAAgB,aAAa,UAAkC,QAAQ,IAAI;AACzE,QAAOA,6BACJ,KAAK,aAAa;EAAE;EAAS,kCAAsB,UAAU,QAAQ;EAAE,EAAE,CACzE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS,CACvC,MAAM,GAAG,MAAM,CACf,KAAK,EAAE,SAAS,eAAe,WAAW,SAAS,SAAS,CAAC;;;;;AAMlE,SAAgB,YAAY,OAAe;CACzC,MAAM,WAAW,EACd,MAAe,EAAE,OAAO,QACxB,MAAe,EAAE,OAAO,OAAO,MACjC;CAED,IAAIC,QAA6B;AAEjC,MAAK,MAAM,UAAU,UAAU;AAC7B,UAAQD,6BAAS,KAAK,OAAO;AAC7B,MAAI,MAAO;;AAGb,KAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,QAAQ;AAE1D,QAAO,WAAW,MAAM;;AAG1B,SAAgB,WAAW,SAAkB,UAAmB;CAE9D,IAAI,YAAY;AAChB,KAAI,QAAQ,SAAS,cACnB,aAAY,YAAY,QAAQ,SAAS,aAAa,GAAG;CAE3D,MAAM,EAAE,QAAQ,0BAA0B;CAG1C,MAAM,eAAe,UAAU,SAAS,SAAS;CAEjD,SAAS,aAAa,EAAE,QAAQ,iBAA8B,EAAE,EAAE;EAChE,IAAI,SAAS;AAEb,MAAI,OAAO;GACT,MAAM,cAAc,SAAS;GAC7B,MAAM,YAAY,SAAS;AAE3B,OAAI,OAAO,gBAAgB,SACzB,OAAM,IAAI,MACR,WAAW,QAAQ,GAAG,WAAW,MAAM,4BAA4B,OAAO,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,KAAK,GACxG;AAGH,OAAI,OAAO,cAAc,SACvB,OAAM,IAAI,MACR,WAAW,QAAQ,GAAG,uDAAuD,MAAM,GACpF;AAGH,YAAS,YAAY;;AAGvB,4CAAqB,uBAAuB;GAC1C,UAAU;GACV;GACD,CAAC;;AAGJ,QAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA,sBAAsB,EAAE,QAAQ,cAAc,GAAG,SAA0B;AACzE,UAAO;IACL;IACA;IACA;IACA,UAAU,aAAa,EAAE,OAAO,CAAC,CAAC,sBAAsB;KACtD,GAAG;KACH,SAAS,QAAQ;KAClB,CAAC;IACH;;EAGH,sBAAsB,EACpB,QAAQ,cACR,GAAG,UACe;AAClB,OAAI,QAAQ,SAAS,cACnB,OAAM,IAAI,MACR,mEACD;AAGH,UAAO;IACL;IACA;IACA,UAAU,aAAa,EAAE,OAAO,CAAC,CAAC,sBAAsB,OAAO;IAChE;;EAGH,oBAAoB,EAAE,MAAM,QAAQ,gBAAmC;AACrE,OAAI,QAAQ,SAAS,cACnB,OAAM,IAAI,MACR,sEACD;AAGH,UAAO;IACL;IACA;IACA,GAAG,aAAa,EAAE,OAAO,CAAC,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACzD;;EAEJ"}
1
+ {"version":3,"file":"index.cjs","names":["defaultUnits: Units","stations","found: Station | undefined"],"sources":["../src/index.ts"],"sourcesContent":["import { getDistance } from \"geolib\";\nimport { stations, type Station } from \"@neaps/tide-database\";\nimport tidePredictor, { type TimeSpan, type ExtremesInput } from \"@neaps/tide-predictor\";\nimport type { GeolibInputCoordinates } from \"geolib/es/types\";\n\ntype Units = \"meters\" | \"feet\";\ntype PredictionOptions = {\n /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */\n datum?: string;\n\n /** Units for returned water levels. Defaults to 'meters'. */\n units?: Units;\n};\n\nexport type ExtremesOptions = ExtremesInput & PredictionOptions;\nexport type TimelineOptions = TimeSpan & PredictionOptions;\nexport type WaterLevelOptions = { time: Date } & PredictionOptions;\n\nconst feetPerMeter = 3.2808399;\nconst defaultUnits: Units = \"meters\";\n\n/**\n * Get extremes prediction using the nearest station to the given position.\n *\n * @example\n * ```ts\n * import { getExtremesPrediction } from 'neaps'\n *\n * const prediction = getExtremesPrediction({\n * latitude: 26.7, // or `lat`\n * longitude: -80.05, // or `lng` or `lon`\n * start: new Date('2025-12-17'),\n * end: new Date('2025-12-18'),\n * datum: 'MLLW', // optional, defaults to MLLW if available\n * })\n */\nexport function getExtremesPrediction(options: GeolibInputCoordinates & ExtremesOptions) {\n return nearestStation(options).getExtremesPrediction(options);\n}\n\n/**\n * Get timeline prediction using the nearest station to the given position.\n */\nexport function getTimelinePrediction(options: GeolibInputCoordinates & TimelineOptions) {\n return nearestStation(options).getTimelinePrediction(options);\n}\n\n/**\n * Get water level at a specific time using the nearest station to the given position.\n */\nexport function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLevelOptions) {\n return nearestStation(options).getWaterLevelAtTime(options);\n}\n\n/**\n * Find the nearest station to the given position.\n */\nexport function nearestStation(position: GeolibInputCoordinates) {\n return stationsNear(position, 1)[0];\n}\n\n/**\n * Find stations near the given position.\n * @param limit Maximum number of stations to return (default: 10)\n */\nexport function stationsNear(position: GeolibInputCoordinates, limit = 10) {\n return stations\n .map((station) => ({ station, distance: getDistance(position, station) }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, limit)\n .map(({ station, distance }) => useStation(station, distance));\n}\n\n/**\n * Find a specific station by its ID or source ID.\n */\nexport function findStation(query: string) {\n const searches = [(s: Station) => s.id === query, (s: Station) => s.source.id === query];\n\n let found: Station | undefined = undefined;\n\n for (const search of searches) {\n found = stations.find(search);\n if (found) break;\n }\n\n if (!found) throw new Error(`Station not found: ${query}`);\n\n return useStation(found);\n}\n\nexport function useStation(station: Station, distance?: number) {\n // If subordinate station, use the reference station for datums and constituents\n let reference = station;\n if (station.type === \"subordinate\") {\n reference = findStation(station.offsets?.reference || \"\");\n }\n const { datums, harmonic_constituents } = reference;\n\n // Use MLLW as the default datum if available\n const defaultDatum = \"MLLW\" in datums ? \"MLLW\" : undefined;\n\n function getPredictor({ datum = defaultDatum }: PredictionOptions = {}) {\n let offset = 0;\n\n if (datum) {\n const datumOffset = datums?.[datum];\n const mslOffset = datums?.[\"MSL\"];\n\n if (typeof datumOffset !== \"number\") {\n throw new Error(\n `Station ${station.id} missing ${datum} datum. Available datums: ${Object.keys(datums || {}).join(\", \")}`,\n );\n }\n\n if (typeof mslOffset !== \"number\") {\n throw new Error(\n `Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`,\n );\n }\n\n offset = mslOffset - datumOffset;\n }\n\n return tidePredictor(harmonic_constituents, { offset });\n }\n\n return {\n ...station,\n distance,\n datums,\n harmonic_constituents,\n defaultDatum,\n getExtremesPrediction({\n datum = defaultDatum,\n units = defaultUnits,\n ...options\n }: ExtremesOptions) {\n const extremes = getPredictor({ datum })\n .getExtremesPrediction({ ...options, offsets: station.offsets })\n .map((e) => toPreferredUnits(e, units));\n\n return { datum, units, station, distance, extremes };\n },\n\n getTimelinePrediction({\n datum = defaultDatum,\n units = defaultUnits,\n ...options\n }: TimelineOptions) {\n if (station.type === \"subordinate\") {\n throw new Error(`Timeline predictions are not supported for subordinate stations.`);\n }\n const timeline = getPredictor({ datum })\n .getTimelinePrediction(options)\n .map((e) => toPreferredUnits(e, units));\n\n return { datum, units, station, distance, timeline };\n },\n\n getWaterLevelAtTime({ time, datum = defaultDatum, units = defaultUnits }: WaterLevelOptions) {\n if (station.type === \"subordinate\") {\n throw new Error(`Water level predictions are not supported for subordinate stations.`);\n }\n\n const prediction = toPreferredUnits(\n getPredictor({ datum }).getWaterLevelAtTime({ time }),\n units,\n );\n\n return { datum, units, station, distance, ...prediction };\n },\n };\n}\n\nfunction toPreferredUnits<T extends { level: number }>(prediction: T, units: Units): T {\n let { level } = prediction;\n if (units === \"feet\") level *= feetPerMeter;\n else if (units !== \"meters\") throw new Error(`Unsupported units: ${units}`);\n return { ...prediction, level };\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,18 +1,22 @@
1
1
  import * as _neaps_tide_predictor0 from "@neaps/tide-predictor";
2
2
  import { ExtremesInput, TimeSpan } from "@neaps/tide-predictor";
3
+ import * as _neaps_tide_database0 from "@neaps/tide-database";
3
4
  import { Station } from "@neaps/tide-database";
4
5
  import { GeolibInputCoordinates } from "geolib/es/types";
5
6
 
6
7
  //#region src/index.d.ts
7
- type DatumOption = {
8
+ type Units = "meters" | "feet";
9
+ type PredictionOptions = {
8
10
  /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */
9
11
  datum?: string;
12
+ /** Units for returned water levels. Defaults to 'meters'. */
13
+ units?: Units;
10
14
  };
11
- type ExtremesOptions = ExtremesInput & DatumOption;
12
- type TimelineOptions = TimeSpan & DatumOption;
15
+ type ExtremesOptions = ExtremesInput & PredictionOptions;
16
+ type TimelineOptions = TimeSpan & PredictionOptions;
13
17
  type WaterLevelOptions = {
14
18
  time: Date;
15
- } & DatumOption;
19
+ } & PredictionOptions;
16
20
  /**
17
21
  * Get extremes prediction using the nearest station to the given position.
18
22
  *
@@ -30,8 +34,9 @@ type WaterLevelOptions = {
30
34
  */
31
35
  declare function getExtremesPrediction(options: GeolibInputCoordinates & ExtremesOptions): {
32
36
  datum: string | undefined;
33
- distance: number | undefined;
37
+ units: Units;
34
38
  station: Station;
39
+ distance: number | undefined;
35
40
  extremes: _neaps_tide_predictor0.Extreme[];
36
41
  };
37
42
  /**
@@ -39,7 +44,9 @@ declare function getExtremesPrediction(options: GeolibInputCoordinates & Extreme
39
44
  */
40
45
  declare function getTimelinePrediction(options: GeolibInputCoordinates & TimelineOptions): {
41
46
  datum: string | undefined;
47
+ units: Units;
42
48
  station: Station;
49
+ distance: number | undefined;
43
50
  timeline: _neaps_tide_predictor0.TimelinePoint[];
44
51
  };
45
52
  /**
@@ -50,7 +57,9 @@ declare function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLeve
50
57
  hour: number;
51
58
  level: number;
52
59
  datum: string | undefined;
60
+ units: Units;
53
61
  station: Station;
62
+ distance: number | undefined;
54
63
  };
55
64
  /**
56
65
  * Find the nearest station to the given position.
@@ -58,47 +67,48 @@ declare function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLeve
58
67
  declare function nearestStation(position: GeolibInputCoordinates): {
59
68
  distance: number | undefined;
60
69
  datums: Record<string, number>;
61
- harmonic_constituents: {
62
- name: string;
63
- description?: string;
64
- amplitude: number;
65
- phase_UTC: number;
66
- phase_local: number;
67
- speed?: number;
68
- }[];
70
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
69
71
  defaultDatum: string | undefined;
70
72
  getExtremesPrediction({
71
73
  datum,
72
- ...input
74
+ units,
75
+ ...options
73
76
  }: ExtremesOptions): {
74
77
  datum: string | undefined;
75
- distance: number | undefined;
78
+ units: Units;
76
79
  station: Station;
80
+ distance: number | undefined;
77
81
  extremes: _neaps_tide_predictor0.Extreme[];
78
82
  };
79
83
  getTimelinePrediction({
80
84
  datum,
81
- ...params
85
+ units,
86
+ ...options
82
87
  }: TimelineOptions): {
83
88
  datum: string | undefined;
89
+ units: Units;
84
90
  station: Station;
91
+ distance: number | undefined;
85
92
  timeline: _neaps_tide_predictor0.TimelinePoint[];
86
93
  };
87
94
  getWaterLevelAtTime({
88
95
  time,
89
- datum
96
+ datum,
97
+ units
90
98
  }: WaterLevelOptions): {
91
99
  time: Date;
92
100
  hour: number;
93
101
  level: number;
94
102
  datum: string | undefined;
103
+ units: Units;
95
104
  station: Station;
105
+ distance: number | undefined;
96
106
  };
97
107
  id: string;
98
108
  name: string;
99
109
  continent: string;
100
110
  country: string;
101
- region: string;
111
+ region?: string;
102
112
  timezone: string;
103
113
  disclaimers: string;
104
114
  type: "reference" | "subordinate";
@@ -109,7 +119,6 @@ declare function nearestStation(position: GeolibInputCoordinates): {
109
119
  id: string;
110
120
  published_harmonics: boolean;
111
121
  url: string;
112
- source_url: string;
113
122
  };
114
123
  license: {
115
124
  type: string;
@@ -137,47 +146,48 @@ declare function nearestStation(position: GeolibInputCoordinates): {
137
146
  declare function stationsNear(position: GeolibInputCoordinates, limit?: number): {
138
147
  distance: number | undefined;
139
148
  datums: Record<string, number>;
140
- harmonic_constituents: {
141
- name: string;
142
- description?: string;
143
- amplitude: number;
144
- phase_UTC: number;
145
- phase_local: number;
146
- speed?: number;
147
- }[];
149
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
148
150
  defaultDatum: string | undefined;
149
151
  getExtremesPrediction({
150
152
  datum,
151
- ...input
153
+ units,
154
+ ...options
152
155
  }: ExtremesOptions): {
153
156
  datum: string | undefined;
154
- distance: number | undefined;
157
+ units: Units;
155
158
  station: Station;
159
+ distance: number | undefined;
156
160
  extremes: _neaps_tide_predictor0.Extreme[];
157
161
  };
158
162
  getTimelinePrediction({
159
163
  datum,
160
- ...params
164
+ units,
165
+ ...options
161
166
  }: TimelineOptions): {
162
167
  datum: string | undefined;
168
+ units: Units;
163
169
  station: Station;
170
+ distance: number | undefined;
164
171
  timeline: _neaps_tide_predictor0.TimelinePoint[];
165
172
  };
166
173
  getWaterLevelAtTime({
167
174
  time,
168
- datum
175
+ datum,
176
+ units
169
177
  }: WaterLevelOptions): {
170
178
  time: Date;
171
179
  hour: number;
172
180
  level: number;
173
181
  datum: string | undefined;
182
+ units: Units;
174
183
  station: Station;
184
+ distance: number | undefined;
175
185
  };
176
186
  id: string;
177
187
  name: string;
178
188
  continent: string;
179
189
  country: string;
180
- region: string;
190
+ region?: string;
181
191
  timezone: string;
182
192
  disclaimers: string;
183
193
  type: "reference" | "subordinate";
@@ -188,7 +198,6 @@ declare function stationsNear(position: GeolibInputCoordinates, limit?: number):
188
198
  id: string;
189
199
  published_harmonics: boolean;
190
200
  url: string;
191
- source_url: string;
192
201
  };
193
202
  license: {
194
203
  type: string;
@@ -215,47 +224,48 @@ declare function stationsNear(position: GeolibInputCoordinates, limit?: number):
215
224
  declare function findStation(query: string): {
216
225
  distance: number | undefined;
217
226
  datums: Record<string, number>;
218
- harmonic_constituents: {
219
- name: string;
220
- description?: string;
221
- amplitude: number;
222
- phase_UTC: number;
223
- phase_local: number;
224
- speed?: number;
225
- }[];
227
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
226
228
  defaultDatum: string | undefined;
227
229
  getExtremesPrediction({
228
230
  datum,
229
- ...input
231
+ units,
232
+ ...options
230
233
  }: ExtremesOptions): {
231
234
  datum: string | undefined;
232
- distance: number | undefined;
235
+ units: Units;
233
236
  station: Station;
237
+ distance: number | undefined;
234
238
  extremes: _neaps_tide_predictor0.Extreme[];
235
239
  };
236
240
  getTimelinePrediction({
237
241
  datum,
238
- ...params
242
+ units,
243
+ ...options
239
244
  }: TimelineOptions): {
240
245
  datum: string | undefined;
246
+ units: Units;
241
247
  station: Station;
248
+ distance: number | undefined;
242
249
  timeline: _neaps_tide_predictor0.TimelinePoint[];
243
250
  };
244
251
  getWaterLevelAtTime({
245
252
  time,
246
- datum
253
+ datum,
254
+ units
247
255
  }: WaterLevelOptions): {
248
256
  time: Date;
249
257
  hour: number;
250
258
  level: number;
251
259
  datum: string | undefined;
260
+ units: Units;
252
261
  station: Station;
262
+ distance: number | undefined;
253
263
  };
254
264
  id: string;
255
265
  name: string;
256
266
  continent: string;
257
267
  country: string;
258
- region: string;
268
+ region?: string;
259
269
  timezone: string;
260
270
  disclaimers: string;
261
271
  type: "reference" | "subordinate";
@@ -266,7 +276,6 @@ declare function findStation(query: string): {
266
276
  id: string;
267
277
  published_harmonics: boolean;
268
278
  url: string;
269
- source_url: string;
270
279
  };
271
280
  license: {
272
281
  type: string;
@@ -290,47 +299,48 @@ declare function findStation(query: string): {
290
299
  declare function useStation(station: Station, distance?: number): {
291
300
  distance: number | undefined;
292
301
  datums: Record<string, number>;
293
- harmonic_constituents: {
294
- name: string;
295
- description?: string;
296
- amplitude: number;
297
- phase_UTC: number;
298
- phase_local: number;
299
- speed?: number;
300
- }[];
302
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
301
303
  defaultDatum: string | undefined;
302
304
  getExtremesPrediction({
303
305
  datum,
304
- ...input
306
+ units,
307
+ ...options
305
308
  }: ExtremesOptions): {
306
309
  datum: string | undefined;
307
- distance: number | undefined;
310
+ units: Units;
308
311
  station: Station;
312
+ distance: number | undefined;
309
313
  extremes: _neaps_tide_predictor0.Extreme[];
310
314
  };
311
315
  getTimelinePrediction({
312
316
  datum,
313
- ...params
317
+ units,
318
+ ...options
314
319
  }: TimelineOptions): {
315
320
  datum: string | undefined;
321
+ units: Units;
316
322
  station: Station;
323
+ distance: number | undefined;
317
324
  timeline: _neaps_tide_predictor0.TimelinePoint[];
318
325
  };
319
326
  getWaterLevelAtTime({
320
327
  time,
321
- datum
328
+ datum,
329
+ units
322
330
  }: WaterLevelOptions): {
323
331
  time: Date;
324
332
  hour: number;
325
333
  level: number;
326
334
  datum: string | undefined;
335
+ units: Units;
327
336
  station: Station;
337
+ distance: number | undefined;
328
338
  };
329
339
  id: string;
330
340
  name: string;
331
341
  continent: string;
332
342
  country: string;
333
- region: string;
343
+ region?: string;
334
344
  timezone: string;
335
345
  disclaimers: string;
336
346
  type: "reference" | "subordinate";
@@ -341,7 +351,6 @@ declare function useStation(station: Station, distance?: number): {
341
351
  id: string;
342
352
  published_harmonics: boolean;
343
353
  url: string;
344
- source_url: string;
345
354
  };
346
355
  license: {
347
356
  type: string;
package/dist/index.d.ts CHANGED
@@ -1,18 +1,22 @@
1
+ import * as _neaps_tide_database0 from "@neaps/tide-database";
1
2
  import { Station } from "@neaps/tide-database";
2
3
  import * as _neaps_tide_predictor0 from "@neaps/tide-predictor";
3
4
  import { ExtremesInput, TimeSpan } from "@neaps/tide-predictor";
4
5
  import { GeolibInputCoordinates } from "geolib/es/types";
5
6
 
6
7
  //#region src/index.d.ts
7
- type DatumOption = {
8
+ type Units = "meters" | "feet";
9
+ type PredictionOptions = {
8
10
  /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */
9
11
  datum?: string;
12
+ /** Units for returned water levels. Defaults to 'meters'. */
13
+ units?: Units;
10
14
  };
11
- type ExtremesOptions = ExtremesInput & DatumOption;
12
- type TimelineOptions = TimeSpan & DatumOption;
15
+ type ExtremesOptions = ExtremesInput & PredictionOptions;
16
+ type TimelineOptions = TimeSpan & PredictionOptions;
13
17
  type WaterLevelOptions = {
14
18
  time: Date;
15
- } & DatumOption;
19
+ } & PredictionOptions;
16
20
  /**
17
21
  * Get extremes prediction using the nearest station to the given position.
18
22
  *
@@ -30,8 +34,9 @@ type WaterLevelOptions = {
30
34
  */
31
35
  declare function getExtremesPrediction(options: GeolibInputCoordinates & ExtremesOptions): {
32
36
  datum: string | undefined;
33
- distance: number | undefined;
37
+ units: Units;
34
38
  station: Station;
39
+ distance: number | undefined;
35
40
  extremes: _neaps_tide_predictor0.Extreme[];
36
41
  };
37
42
  /**
@@ -39,7 +44,9 @@ declare function getExtremesPrediction(options: GeolibInputCoordinates & Extreme
39
44
  */
40
45
  declare function getTimelinePrediction(options: GeolibInputCoordinates & TimelineOptions): {
41
46
  datum: string | undefined;
47
+ units: Units;
42
48
  station: Station;
49
+ distance: number | undefined;
43
50
  timeline: _neaps_tide_predictor0.TimelinePoint[];
44
51
  };
45
52
  /**
@@ -50,7 +57,9 @@ declare function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLeve
50
57
  hour: number;
51
58
  level: number;
52
59
  datum: string | undefined;
60
+ units: Units;
53
61
  station: Station;
62
+ distance: number | undefined;
54
63
  };
55
64
  /**
56
65
  * Find the nearest station to the given position.
@@ -58,47 +67,48 @@ declare function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLeve
58
67
  declare function nearestStation(position: GeolibInputCoordinates): {
59
68
  distance: number | undefined;
60
69
  datums: Record<string, number>;
61
- harmonic_constituents: {
62
- name: string;
63
- description?: string;
64
- amplitude: number;
65
- phase_UTC: number;
66
- phase_local: number;
67
- speed?: number;
68
- }[];
70
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
69
71
  defaultDatum: string | undefined;
70
72
  getExtremesPrediction({
71
73
  datum,
72
- ...input
74
+ units,
75
+ ...options
73
76
  }: ExtremesOptions): {
74
77
  datum: string | undefined;
75
- distance: number | undefined;
78
+ units: Units;
76
79
  station: Station;
80
+ distance: number | undefined;
77
81
  extremes: _neaps_tide_predictor0.Extreme[];
78
82
  };
79
83
  getTimelinePrediction({
80
84
  datum,
81
- ...params
85
+ units,
86
+ ...options
82
87
  }: TimelineOptions): {
83
88
  datum: string | undefined;
89
+ units: Units;
84
90
  station: Station;
91
+ distance: number | undefined;
85
92
  timeline: _neaps_tide_predictor0.TimelinePoint[];
86
93
  };
87
94
  getWaterLevelAtTime({
88
95
  time,
89
- datum
96
+ datum,
97
+ units
90
98
  }: WaterLevelOptions): {
91
99
  time: Date;
92
100
  hour: number;
93
101
  level: number;
94
102
  datum: string | undefined;
103
+ units: Units;
95
104
  station: Station;
105
+ distance: number | undefined;
96
106
  };
97
107
  id: string;
98
108
  name: string;
99
109
  continent: string;
100
110
  country: string;
101
- region: string;
111
+ region?: string;
102
112
  timezone: string;
103
113
  disclaimers: string;
104
114
  type: "reference" | "subordinate";
@@ -109,7 +119,6 @@ declare function nearestStation(position: GeolibInputCoordinates): {
109
119
  id: string;
110
120
  published_harmonics: boolean;
111
121
  url: string;
112
- source_url: string;
113
122
  };
114
123
  license: {
115
124
  type: string;
@@ -137,47 +146,48 @@ declare function nearestStation(position: GeolibInputCoordinates): {
137
146
  declare function stationsNear(position: GeolibInputCoordinates, limit?: number): {
138
147
  distance: number | undefined;
139
148
  datums: Record<string, number>;
140
- harmonic_constituents: {
141
- name: string;
142
- description?: string;
143
- amplitude: number;
144
- phase_UTC: number;
145
- phase_local: number;
146
- speed?: number;
147
- }[];
149
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
148
150
  defaultDatum: string | undefined;
149
151
  getExtremesPrediction({
150
152
  datum,
151
- ...input
153
+ units,
154
+ ...options
152
155
  }: ExtremesOptions): {
153
156
  datum: string | undefined;
154
- distance: number | undefined;
157
+ units: Units;
155
158
  station: Station;
159
+ distance: number | undefined;
156
160
  extremes: _neaps_tide_predictor0.Extreme[];
157
161
  };
158
162
  getTimelinePrediction({
159
163
  datum,
160
- ...params
164
+ units,
165
+ ...options
161
166
  }: TimelineOptions): {
162
167
  datum: string | undefined;
168
+ units: Units;
163
169
  station: Station;
170
+ distance: number | undefined;
164
171
  timeline: _neaps_tide_predictor0.TimelinePoint[];
165
172
  };
166
173
  getWaterLevelAtTime({
167
174
  time,
168
- datum
175
+ datum,
176
+ units
169
177
  }: WaterLevelOptions): {
170
178
  time: Date;
171
179
  hour: number;
172
180
  level: number;
173
181
  datum: string | undefined;
182
+ units: Units;
174
183
  station: Station;
184
+ distance: number | undefined;
175
185
  };
176
186
  id: string;
177
187
  name: string;
178
188
  continent: string;
179
189
  country: string;
180
- region: string;
190
+ region?: string;
181
191
  timezone: string;
182
192
  disclaimers: string;
183
193
  type: "reference" | "subordinate";
@@ -188,7 +198,6 @@ declare function stationsNear(position: GeolibInputCoordinates, limit?: number):
188
198
  id: string;
189
199
  published_harmonics: boolean;
190
200
  url: string;
191
- source_url: string;
192
201
  };
193
202
  license: {
194
203
  type: string;
@@ -215,47 +224,48 @@ declare function stationsNear(position: GeolibInputCoordinates, limit?: number):
215
224
  declare function findStation(query: string): {
216
225
  distance: number | undefined;
217
226
  datums: Record<string, number>;
218
- harmonic_constituents: {
219
- name: string;
220
- description?: string;
221
- amplitude: number;
222
- phase_UTC: number;
223
- phase_local: number;
224
- speed?: number;
225
- }[];
227
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
226
228
  defaultDatum: string | undefined;
227
229
  getExtremesPrediction({
228
230
  datum,
229
- ...input
231
+ units,
232
+ ...options
230
233
  }: ExtremesOptions): {
231
234
  datum: string | undefined;
232
- distance: number | undefined;
235
+ units: Units;
233
236
  station: Station;
237
+ distance: number | undefined;
234
238
  extremes: _neaps_tide_predictor0.Extreme[];
235
239
  };
236
240
  getTimelinePrediction({
237
241
  datum,
238
- ...params
242
+ units,
243
+ ...options
239
244
  }: TimelineOptions): {
240
245
  datum: string | undefined;
246
+ units: Units;
241
247
  station: Station;
248
+ distance: number | undefined;
242
249
  timeline: _neaps_tide_predictor0.TimelinePoint[];
243
250
  };
244
251
  getWaterLevelAtTime({
245
252
  time,
246
- datum
253
+ datum,
254
+ units
247
255
  }: WaterLevelOptions): {
248
256
  time: Date;
249
257
  hour: number;
250
258
  level: number;
251
259
  datum: string | undefined;
260
+ units: Units;
252
261
  station: Station;
262
+ distance: number | undefined;
253
263
  };
254
264
  id: string;
255
265
  name: string;
256
266
  continent: string;
257
267
  country: string;
258
- region: string;
268
+ region?: string;
259
269
  timezone: string;
260
270
  disclaimers: string;
261
271
  type: "reference" | "subordinate";
@@ -266,7 +276,6 @@ declare function findStation(query: string): {
266
276
  id: string;
267
277
  published_harmonics: boolean;
268
278
  url: string;
269
- source_url: string;
270
279
  };
271
280
  license: {
272
281
  type: string;
@@ -290,47 +299,48 @@ declare function findStation(query: string): {
290
299
  declare function useStation(station: Station, distance?: number): {
291
300
  distance: number | undefined;
292
301
  datums: Record<string, number>;
293
- harmonic_constituents: {
294
- name: string;
295
- description?: string;
296
- amplitude: number;
297
- phase_UTC: number;
298
- phase_local: number;
299
- speed?: number;
300
- }[];
302
+ harmonic_constituents: _neaps_tide_database0.HarmonicConstituent[];
301
303
  defaultDatum: string | undefined;
302
304
  getExtremesPrediction({
303
305
  datum,
304
- ...input
306
+ units,
307
+ ...options
305
308
  }: ExtremesOptions): {
306
309
  datum: string | undefined;
307
- distance: number | undefined;
310
+ units: Units;
308
311
  station: Station;
312
+ distance: number | undefined;
309
313
  extremes: _neaps_tide_predictor0.Extreme[];
310
314
  };
311
315
  getTimelinePrediction({
312
316
  datum,
313
- ...params
317
+ units,
318
+ ...options
314
319
  }: TimelineOptions): {
315
320
  datum: string | undefined;
321
+ units: Units;
316
322
  station: Station;
323
+ distance: number | undefined;
317
324
  timeline: _neaps_tide_predictor0.TimelinePoint[];
318
325
  };
319
326
  getWaterLevelAtTime({
320
327
  time,
321
- datum
328
+ datum,
329
+ units
322
330
  }: WaterLevelOptions): {
323
331
  time: Date;
324
332
  hour: number;
325
333
  level: number;
326
334
  datum: string | undefined;
335
+ units: Units;
327
336
  station: Station;
337
+ distance: number | undefined;
328
338
  };
329
339
  id: string;
330
340
  name: string;
331
341
  continent: string;
332
342
  country: string;
333
- region: string;
343
+ region?: string;
334
344
  timezone: string;
335
345
  disclaimers: string;
336
346
  type: "reference" | "subordinate";
@@ -341,7 +351,6 @@ declare function useStation(station: Station, distance?: number): {
341
351
  id: string;
342
352
  published_harmonics: boolean;
343
353
  url: string;
344
- source_url: string;
345
354
  };
346
355
  license: {
347
356
  type: string;
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { getDistance } from "geolib";
2
- import stations from "@neaps/tide-database";
2
+ import { stations } from "@neaps/tide-database";
3
3
  import tidePredictor from "@neaps/tide-predictor";
4
4
 
5
5
  //#region src/index.ts
6
+ const feetPerMeter = 3.2808399;
7
+ const defaultUnits = "meters";
6
8
  /**
7
9
  * Get extremes prediction using the nearest station to the given position.
8
10
  *
@@ -76,10 +78,7 @@ function useStation(station, distance) {
76
78
  if (typeof mslOffset !== "number") throw new Error(`Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`);
77
79
  offset = mslOffset - datumOffset;
78
80
  }
79
- return tidePredictor(harmonic_constituents, {
80
- phaseKey: "phase_UTC",
81
- offset
82
- });
81
+ return tidePredictor(harmonic_constituents, { offset });
83
82
  }
84
83
  return {
85
84
  ...station,
@@ -87,35 +86,49 @@ function useStation(station, distance) {
87
86
  datums,
88
87
  harmonic_constituents,
89
88
  defaultDatum,
90
- getExtremesPrediction({ datum = defaultDatum, ...input }) {
89
+ getExtremesPrediction({ datum = defaultDatum, units = defaultUnits, ...options }) {
91
90
  return {
92
91
  datum,
93
- distance,
92
+ units,
94
93
  station,
94
+ distance,
95
95
  extremes: getPredictor({ datum }).getExtremesPrediction({
96
- ...input,
96
+ ...options,
97
97
  offsets: station.offsets
98
- })
98
+ }).map((e) => toPreferredUnits(e, units))
99
99
  };
100
100
  },
101
- getTimelinePrediction({ datum = defaultDatum, ...params }) {
101
+ getTimelinePrediction({ datum = defaultDatum, units = defaultUnits, ...options }) {
102
102
  if (station.type === "subordinate") throw new Error(`Timeline predictions are not supported for subordinate stations.`);
103
103
  return {
104
104
  datum,
105
+ units,
105
106
  station,
106
- timeline: getPredictor({ datum }).getTimelinePrediction(params)
107
+ distance,
108
+ timeline: getPredictor({ datum }).getTimelinePrediction(options).map((e) => toPreferredUnits(e, units))
107
109
  };
108
110
  },
109
- getWaterLevelAtTime({ time, datum = defaultDatum }) {
111
+ getWaterLevelAtTime({ time, datum = defaultDatum, units = defaultUnits }) {
110
112
  if (station.type === "subordinate") throw new Error(`Water level predictions are not supported for subordinate stations.`);
111
113
  return {
112
114
  datum,
115
+ units,
113
116
  station,
114
- ...getPredictor({ datum }).getWaterLevelAtTime({ time })
117
+ distance,
118
+ ...toPreferredUnits(getPredictor({ datum }).getWaterLevelAtTime({ time }), units)
115
119
  };
116
120
  }
117
121
  };
118
122
  }
123
+ function toPreferredUnits(prediction, units) {
124
+ let { level } = prediction;
125
+ if (units === "feet") level *= feetPerMeter;
126
+ else if (units !== "meters") throw new Error(`Unsupported units: ${units}`);
127
+ return {
128
+ ...prediction,
129
+ level
130
+ };
131
+ }
119
132
 
120
133
  //#endregion
121
134
  export { findStation, getExtremesPrediction, getTimelinePrediction, getWaterLevelAtTime, nearestStation, stationsNear, useStation };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["found: Station | undefined"],"sources":["../src/index.ts"],"sourcesContent":["import { getDistance } from 'geolib'\nimport stations, { type Station } from '@neaps/tide-database'\nimport tidePredictor, {\n type TimeSpan,\n type ExtremesInput\n} from '@neaps/tide-predictor'\nimport type { GeolibInputCoordinates } from 'geolib/es/types'\n\ntype DatumOption = {\n /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */\n datum?: string\n}\n\nexport type ExtremesOptions = ExtremesInput & DatumOption\nexport type TimelineOptions = TimeSpan & DatumOption\nexport type WaterLevelOptions = { time: Date } & DatumOption\n\n/**\n * Get extremes prediction using the nearest station to the given position.\n *\n * @example\n * ```ts\n * import { getExtremesPrediction } from 'neaps'\n *\n * const prediction = getExtremesPrediction({\n * latitude: 26.7, // or `lat`\n * longitude: -80.05, // or `lng` or `lon`\n * start: new Date('2025-12-17'),\n * end: new Date('2025-12-18'),\n * datum: 'MLLW', // optional, defaults to MLLW if available\n * })\n */\nexport function getExtremesPrediction(\n options: GeolibInputCoordinates & ExtremesOptions\n) {\n return nearestStation(options).getExtremesPrediction(options)\n}\n\n/**\n * Get timeline prediction using the nearest station to the given position.\n */\nexport function getTimelinePrediction(\n options: GeolibInputCoordinates & TimelineOptions\n) {\n return nearestStation(options).getTimelinePrediction(options)\n}\n\n/**\n * Get water level at a specific time using the nearest station to the given position.\n */\nexport function getWaterLevelAtTime(\n options: GeolibInputCoordinates & WaterLevelOptions\n) {\n return nearestStation(options).getWaterLevelAtTime(options)\n}\n\n/**\n * Find the nearest station to the given position.\n */\nexport function nearestStation(position: GeolibInputCoordinates) {\n return stationsNear(position, 1)[0]\n}\n\n/**\n * Find stations near the given position.\n * @param limit Maximum number of stations to return (default: 10)\n */\nexport function stationsNear(position: GeolibInputCoordinates, limit = 10) {\n return stations\n .map((station) => ({ station, distance: getDistance(position, station) }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, limit)\n .map(({ station, distance }) => useStation(station, distance))\n}\n\n/**\n * Find a specific station by its ID or source ID.\n */\nexport function findStation(query: string) {\n const searches = [\n (s: Station) => s.id === query,\n (s: Station) => s.source.id === query\n ]\n\n let found: Station | undefined = undefined\n\n for (const search of searches) {\n found = stations.find(search)\n if (found) break\n }\n\n if (!found) throw new Error(`Station not found: ${query}`)\n\n return useStation(found)\n}\n\nexport function useStation(station: Station, distance?: number) {\n // If subordinate station, use the reference station for datums and constituents\n let reference = station\n if (station.type === 'subordinate') {\n reference = findStation(station.offsets?.reference || '')\n }\n const { datums, harmonic_constituents } = reference\n\n // Use MLLW as the default datum if available\n const defaultDatum = 'MLLW' in datums ? 'MLLW' : undefined\n\n function getPredictor({ datum = defaultDatum }: DatumOption = {}) {\n let offset = 0\n\n if (datum) {\n const datumOffset = datums?.[datum]\n const mslOffset = datums?.['MSL']\n\n if (typeof datumOffset !== 'number') {\n throw new Error(\n `Station ${station.id} missing ${datum} datum. Available datums: ${Object.keys(datums || {}).join(', ')}`\n )\n }\n\n if (typeof mslOffset !== 'number') {\n throw new Error(\n `Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`\n )\n }\n\n offset = mslOffset - datumOffset\n }\n\n return tidePredictor(harmonic_constituents, {\n phaseKey: 'phase_UTC',\n offset\n })\n }\n\n return {\n ...station,\n distance,\n datums,\n harmonic_constituents,\n defaultDatum,\n getExtremesPrediction({ datum = defaultDatum, ...input }: ExtremesOptions) {\n return {\n datum,\n distance,\n station,\n extremes: getPredictor({ datum }).getExtremesPrediction({\n ...input,\n offsets: station.offsets\n })\n }\n },\n\n getTimelinePrediction({\n datum = defaultDatum,\n ...params\n }: TimelineOptions) {\n if (station.type === 'subordinate') {\n throw new Error(\n `Timeline predictions are not supported for subordinate stations.`\n )\n }\n\n return {\n datum,\n station,\n timeline: getPredictor({ datum }).getTimelinePrediction(params)\n }\n },\n\n getWaterLevelAtTime({ time, datum = defaultDatum }: WaterLevelOptions) {\n if (station.type === 'subordinate') {\n throw new Error(\n `Water level predictions are not supported for subordinate stations.`\n )\n }\n\n return {\n datum,\n station,\n ...getPredictor({ datum }).getWaterLevelAtTime({ time })\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,sBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,sBAAsB,QAAQ;;;;;AAM/D,SAAgB,sBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,sBAAsB,QAAQ;;;;;AAM/D,SAAgB,oBACd,SACA;AACA,QAAO,eAAe,QAAQ,CAAC,oBAAoB,QAAQ;;;;;AAM7D,SAAgB,eAAe,UAAkC;AAC/D,QAAO,aAAa,UAAU,EAAE,CAAC;;;;;;AAOnC,SAAgB,aAAa,UAAkC,QAAQ,IAAI;AACzE,QAAO,SACJ,KAAK,aAAa;EAAE;EAAS,UAAU,YAAY,UAAU,QAAQ;EAAE,EAAE,CACzE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS,CACvC,MAAM,GAAG,MAAM,CACf,KAAK,EAAE,SAAS,eAAe,WAAW,SAAS,SAAS,CAAC;;;;;AAMlE,SAAgB,YAAY,OAAe;CACzC,MAAM,WAAW,EACd,MAAe,EAAE,OAAO,QACxB,MAAe,EAAE,OAAO,OAAO,MACjC;CAED,IAAIA,QAA6B;AAEjC,MAAK,MAAM,UAAU,UAAU;AAC7B,UAAQ,SAAS,KAAK,OAAO;AAC7B,MAAI,MAAO;;AAGb,KAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,QAAQ;AAE1D,QAAO,WAAW,MAAM;;AAG1B,SAAgB,WAAW,SAAkB,UAAmB;CAE9D,IAAI,YAAY;AAChB,KAAI,QAAQ,SAAS,cACnB,aAAY,YAAY,QAAQ,SAAS,aAAa,GAAG;CAE3D,MAAM,EAAE,QAAQ,0BAA0B;CAG1C,MAAM,eAAe,UAAU,SAAS,SAAS;CAEjD,SAAS,aAAa,EAAE,QAAQ,iBAA8B,EAAE,EAAE;EAChE,IAAI,SAAS;AAEb,MAAI,OAAO;GACT,MAAM,cAAc,SAAS;GAC7B,MAAM,YAAY,SAAS;AAE3B,OAAI,OAAO,gBAAgB,SACzB,OAAM,IAAI,MACR,WAAW,QAAQ,GAAG,WAAW,MAAM,4BAA4B,OAAO,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,KAAK,GACxG;AAGH,OAAI,OAAO,cAAc,SACvB,OAAM,IAAI,MACR,WAAW,QAAQ,GAAG,uDAAuD,MAAM,GACpF;AAGH,YAAS,YAAY;;AAGvB,SAAO,cAAc,uBAAuB;GAC1C,UAAU;GACV;GACD,CAAC;;AAGJ,QAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA,sBAAsB,EAAE,QAAQ,cAAc,GAAG,SAA0B;AACzE,UAAO;IACL;IACA;IACA;IACA,UAAU,aAAa,EAAE,OAAO,CAAC,CAAC,sBAAsB;KACtD,GAAG;KACH,SAAS,QAAQ;KAClB,CAAC;IACH;;EAGH,sBAAsB,EACpB,QAAQ,cACR,GAAG,UACe;AAClB,OAAI,QAAQ,SAAS,cACnB,OAAM,IAAI,MACR,mEACD;AAGH,UAAO;IACL;IACA;IACA,UAAU,aAAa,EAAE,OAAO,CAAC,CAAC,sBAAsB,OAAO;IAChE;;EAGH,oBAAoB,EAAE,MAAM,QAAQ,gBAAmC;AACrE,OAAI,QAAQ,SAAS,cACnB,OAAM,IAAI,MACR,sEACD;AAGH,UAAO;IACL;IACA;IACA,GAAG,aAAa,EAAE,OAAO,CAAC,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACzD;;EAEJ"}
1
+ {"version":3,"file":"index.js","names":["defaultUnits: Units","found: Station | undefined"],"sources":["../src/index.ts"],"sourcesContent":["import { getDistance } from \"geolib\";\nimport { stations, type Station } from \"@neaps/tide-database\";\nimport tidePredictor, { type TimeSpan, type ExtremesInput } from \"@neaps/tide-predictor\";\nimport type { GeolibInputCoordinates } from \"geolib/es/types\";\n\ntype Units = \"meters\" | \"feet\";\ntype PredictionOptions = {\n /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */\n datum?: string;\n\n /** Units for returned water levels. Defaults to 'meters'. */\n units?: Units;\n};\n\nexport type ExtremesOptions = ExtremesInput & PredictionOptions;\nexport type TimelineOptions = TimeSpan & PredictionOptions;\nexport type WaterLevelOptions = { time: Date } & PredictionOptions;\n\nconst feetPerMeter = 3.2808399;\nconst defaultUnits: Units = \"meters\";\n\n/**\n * Get extremes prediction using the nearest station to the given position.\n *\n * @example\n * ```ts\n * import { getExtremesPrediction } from 'neaps'\n *\n * const prediction = getExtremesPrediction({\n * latitude: 26.7, // or `lat`\n * longitude: -80.05, // or `lng` or `lon`\n * start: new Date('2025-12-17'),\n * end: new Date('2025-12-18'),\n * datum: 'MLLW', // optional, defaults to MLLW if available\n * })\n */\nexport function getExtremesPrediction(options: GeolibInputCoordinates & ExtremesOptions) {\n return nearestStation(options).getExtremesPrediction(options);\n}\n\n/**\n * Get timeline prediction using the nearest station to the given position.\n */\nexport function getTimelinePrediction(options: GeolibInputCoordinates & TimelineOptions) {\n return nearestStation(options).getTimelinePrediction(options);\n}\n\n/**\n * Get water level at a specific time using the nearest station to the given position.\n */\nexport function getWaterLevelAtTime(options: GeolibInputCoordinates & WaterLevelOptions) {\n return nearestStation(options).getWaterLevelAtTime(options);\n}\n\n/**\n * Find the nearest station to the given position.\n */\nexport function nearestStation(position: GeolibInputCoordinates) {\n return stationsNear(position, 1)[0];\n}\n\n/**\n * Find stations near the given position.\n * @param limit Maximum number of stations to return (default: 10)\n */\nexport function stationsNear(position: GeolibInputCoordinates, limit = 10) {\n return stations\n .map((station) => ({ station, distance: getDistance(position, station) }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, limit)\n .map(({ station, distance }) => useStation(station, distance));\n}\n\n/**\n * Find a specific station by its ID or source ID.\n */\nexport function findStation(query: string) {\n const searches = [(s: Station) => s.id === query, (s: Station) => s.source.id === query];\n\n let found: Station | undefined = undefined;\n\n for (const search of searches) {\n found = stations.find(search);\n if (found) break;\n }\n\n if (!found) throw new Error(`Station not found: ${query}`);\n\n return useStation(found);\n}\n\nexport function useStation(station: Station, distance?: number) {\n // If subordinate station, use the reference station for datums and constituents\n let reference = station;\n if (station.type === \"subordinate\") {\n reference = findStation(station.offsets?.reference || \"\");\n }\n const { datums, harmonic_constituents } = reference;\n\n // Use MLLW as the default datum if available\n const defaultDatum = \"MLLW\" in datums ? \"MLLW\" : undefined;\n\n function getPredictor({ datum = defaultDatum }: PredictionOptions = {}) {\n let offset = 0;\n\n if (datum) {\n const datumOffset = datums?.[datum];\n const mslOffset = datums?.[\"MSL\"];\n\n if (typeof datumOffset !== \"number\") {\n throw new Error(\n `Station ${station.id} missing ${datum} datum. Available datums: ${Object.keys(datums || {}).join(\", \")}`,\n );\n }\n\n if (typeof mslOffset !== \"number\") {\n throw new Error(\n `Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`,\n );\n }\n\n offset = mslOffset - datumOffset;\n }\n\n return tidePredictor(harmonic_constituents, { offset });\n }\n\n return {\n ...station,\n distance,\n datums,\n harmonic_constituents,\n defaultDatum,\n getExtremesPrediction({\n datum = defaultDatum,\n units = defaultUnits,\n ...options\n }: ExtremesOptions) {\n const extremes = getPredictor({ datum })\n .getExtremesPrediction({ ...options, offsets: station.offsets })\n .map((e) => toPreferredUnits(e, units));\n\n return { datum, units, station, distance, extremes };\n },\n\n getTimelinePrediction({\n datum = defaultDatum,\n units = defaultUnits,\n ...options\n }: TimelineOptions) {\n if (station.type === \"subordinate\") {\n throw new Error(`Timeline predictions are not supported for subordinate stations.`);\n }\n const timeline = getPredictor({ datum })\n .getTimelinePrediction(options)\n .map((e) => toPreferredUnits(e, units));\n\n return { datum, units, station, distance, timeline };\n },\n\n getWaterLevelAtTime({ time, datum = defaultDatum, units = defaultUnits }: WaterLevelOptions) {\n if (station.type === \"subordinate\") {\n throw new Error(`Water level predictions are not supported for subordinate stations.`);\n }\n\n const prediction = toPreferredUnits(\n getPredictor({ datum }).getWaterLevelAtTime({ time }),\n units,\n );\n\n return { datum, units, station, distance, ...prediction };\n },\n };\n}\n\nfunction toPreferredUnits<T extends { level: number }>(prediction: T, units: Units): T {\n let { level } = prediction;\n if (units === \"feet\") level *= feetPerMeter;\n else if (units !== \"meters\") throw new Error(`Unsupported units: ${units}`);\n return { ...prediction, level };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neaps",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Tide predictions",
5
5
  "keywords": [
6
6
  "tides",
@@ -18,17 +18,24 @@
18
18
  "license": "MIT",
19
19
  "author": "Brandon Keepers <brandon@openwaters.io>",
20
20
  "type": "module",
21
- "main": "src/index.ts",
22
- "scripts": {
23
- "build": "tsdown",
24
- "prepack": "npm run build"
21
+ "main": "dist/index.cjs",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.cjs"
27
+ }
25
28
  },
26
29
  "files": [
27
30
  "dist"
28
31
  ],
32
+ "scripts": {
33
+ "build": "tsdown",
34
+ "prepack": "npm run build"
35
+ },
29
36
  "dependencies": {
30
- "@neaps/tide-predictor": "^0.2.0",
31
- "@neaps/tide-database": "^0.0",
37
+ "@neaps/tide-database": "0.2",
38
+ "@neaps/tide-predictor": "^0.3.0",
32
39
  "geolib": "^3.3.4"
33
40
  }
34
41
  }
package/src/index.ts DELETED
@@ -1,185 +0,0 @@
1
- import { getDistance } from 'geolib'
2
- import stations, { type Station } from '@neaps/tide-database'
3
- import tidePredictor, {
4
- type TimeSpan,
5
- type ExtremesInput
6
- } from '@neaps/tide-predictor'
7
- import type { GeolibInputCoordinates } from 'geolib/es/types'
8
-
9
- type DatumOption = {
10
- /** Datum to return predictions in. Defaults to 'MLLW' if available for the nearest station. */
11
- datum?: string
12
- }
13
-
14
- export type ExtremesOptions = ExtremesInput & DatumOption
15
- export type TimelineOptions = TimeSpan & DatumOption
16
- export type WaterLevelOptions = { time: Date } & DatumOption
17
-
18
- /**
19
- * Get extremes prediction using the nearest station to the given position.
20
- *
21
- * @example
22
- * ```ts
23
- * import { getExtremesPrediction } from 'neaps'
24
- *
25
- * const prediction = getExtremesPrediction({
26
- * latitude: 26.7, // or `lat`
27
- * longitude: -80.05, // or `lng` or `lon`
28
- * start: new Date('2025-12-17'),
29
- * end: new Date('2025-12-18'),
30
- * datum: 'MLLW', // optional, defaults to MLLW if available
31
- * })
32
- */
33
- export function getExtremesPrediction(
34
- options: GeolibInputCoordinates & ExtremesOptions
35
- ) {
36
- return nearestStation(options).getExtremesPrediction(options)
37
- }
38
-
39
- /**
40
- * Get timeline prediction using the nearest station to the given position.
41
- */
42
- export function getTimelinePrediction(
43
- options: GeolibInputCoordinates & TimelineOptions
44
- ) {
45
- return nearestStation(options).getTimelinePrediction(options)
46
- }
47
-
48
- /**
49
- * Get water level at a specific time using the nearest station to the given position.
50
- */
51
- export function getWaterLevelAtTime(
52
- options: GeolibInputCoordinates & WaterLevelOptions
53
- ) {
54
- return nearestStation(options).getWaterLevelAtTime(options)
55
- }
56
-
57
- /**
58
- * Find the nearest station to the given position.
59
- */
60
- export function nearestStation(position: GeolibInputCoordinates) {
61
- return stationsNear(position, 1)[0]
62
- }
63
-
64
- /**
65
- * Find stations near the given position.
66
- * @param limit Maximum number of stations to return (default: 10)
67
- */
68
- export function stationsNear(position: GeolibInputCoordinates, limit = 10) {
69
- return stations
70
- .map((station) => ({ station, distance: getDistance(position, station) }))
71
- .sort((a, b) => a.distance - b.distance)
72
- .slice(0, limit)
73
- .map(({ station, distance }) => useStation(station, distance))
74
- }
75
-
76
- /**
77
- * Find a specific station by its ID or source ID.
78
- */
79
- export function findStation(query: string) {
80
- const searches = [
81
- (s: Station) => s.id === query,
82
- (s: Station) => s.source.id === query
83
- ]
84
-
85
- let found: Station | undefined = undefined
86
-
87
- for (const search of searches) {
88
- found = stations.find(search)
89
- if (found) break
90
- }
91
-
92
- if (!found) throw new Error(`Station not found: ${query}`)
93
-
94
- return useStation(found)
95
- }
96
-
97
- export function useStation(station: Station, distance?: number) {
98
- // If subordinate station, use the reference station for datums and constituents
99
- let reference = station
100
- if (station.type === 'subordinate') {
101
- reference = findStation(station.offsets?.reference || '')
102
- }
103
- const { datums, harmonic_constituents } = reference
104
-
105
- // Use MLLW as the default datum if available
106
- const defaultDatum = 'MLLW' in datums ? 'MLLW' : undefined
107
-
108
- function getPredictor({ datum = defaultDatum }: DatumOption = {}) {
109
- let offset = 0
110
-
111
- if (datum) {
112
- const datumOffset = datums?.[datum]
113
- const mslOffset = datums?.['MSL']
114
-
115
- if (typeof datumOffset !== 'number') {
116
- throw new Error(
117
- `Station ${station.id} missing ${datum} datum. Available datums: ${Object.keys(datums || {}).join(', ')}`
118
- )
119
- }
120
-
121
- if (typeof mslOffset !== 'number') {
122
- throw new Error(
123
- `Station ${station.id} missing MSL datum, so predictions can't be given in ${datum}.`
124
- )
125
- }
126
-
127
- offset = mslOffset - datumOffset
128
- }
129
-
130
- return tidePredictor(harmonic_constituents, {
131
- phaseKey: 'phase_UTC',
132
- offset
133
- })
134
- }
135
-
136
- return {
137
- ...station,
138
- distance,
139
- datums,
140
- harmonic_constituents,
141
- defaultDatum,
142
- getExtremesPrediction({ datum = defaultDatum, ...input }: ExtremesOptions) {
143
- return {
144
- datum,
145
- distance,
146
- station,
147
- extremes: getPredictor({ datum }).getExtremesPrediction({
148
- ...input,
149
- offsets: station.offsets
150
- })
151
- }
152
- },
153
-
154
- getTimelinePrediction({
155
- datum = defaultDatum,
156
- ...params
157
- }: TimelineOptions) {
158
- if (station.type === 'subordinate') {
159
- throw new Error(
160
- `Timeline predictions are not supported for subordinate stations.`
161
- )
162
- }
163
-
164
- return {
165
- datum,
166
- station,
167
- timeline: getPredictor({ datum }).getTimelinePrediction(params)
168
- }
169
- },
170
-
171
- getWaterLevelAtTime({ time, datum = defaultDatum }: WaterLevelOptions) {
172
- if (station.type === 'subordinate') {
173
- throw new Error(
174
- `Water level predictions are not supported for subordinate stations.`
175
- )
176
- }
177
-
178
- return {
179
- datum,
180
- station,
181
- ...getPredictor({ datum }).getWaterLevelAtTime({ time })
182
- }
183
- }
184
- }
185
- }