@neaps/tide-predictor 0.0.4
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/.eslintrc.js +22 -0
- package/.github/workflows/test.yml +15 -0
- package/.prettierrc +4 -0
- package/Gruntfile.js +87 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/babel.config.js +9 -0
- package/dist/tide-predictor.js +1013 -0
- package/examples/browser/index.html +51 -0
- package/jest.config.js +14 -0
- package/lib/astronomy/coefficients.js +31 -0
- package/lib/astronomy/constants.js +10 -0
- package/lib/astronomy/index.js +199 -0
- package/lib/constituents/compound-constituent.js +67 -0
- package/lib/constituents/constituent.js +74 -0
- package/lib/constituents/index.js +140 -0
- package/lib/harmonics/index.js +113 -0
- package/lib/harmonics/prediction.js +195 -0
- package/lib/index.es6.js +1005 -0
- package/lib/index.js +53 -0
- package/lib/node-corrections/index.js +147 -0
- package/package.json +45 -0
- package/rollup.config.js +21 -0
- package/src/__mocks__/constituents.js +335 -0
- package/src/__mocks__/secondary-station.js +11 -0
- package/src/__tests__/index.js +81 -0
- package/src/__tests__/noaa.js +92 -0
- package/src/astronomy/__tests__/coefficients.js +12 -0
- package/src/astronomy/__tests__/index.js +96 -0
- package/src/astronomy/coefficients.js +72 -0
- package/src/astronomy/constants.js +4 -0
- package/src/astronomy/index.js +201 -0
- package/src/constituents/__tests__/compound-constituent.js +44 -0
- package/src/constituents/__tests__/constituent.js +65 -0
- package/src/constituents/__tests__/index.js +34 -0
- package/src/constituents/compound-constituent.js +55 -0
- package/src/constituents/constituent.js +74 -0
- package/src/constituents/index.js +119 -0
- package/src/harmonics/__mocks__/water-levels.js +0 -0
- package/src/harmonics/__tests__/index.js +123 -0
- package/src/harmonics/__tests__/prediction.js +148 -0
- package/src/harmonics/index.js +87 -0
- package/src/harmonics/prediction.js +175 -0
- package/src/index.js +45 -0
- package/src/node-corrections/__tests__/index.js +114 -0
- package/src/node-corrections/index.js +208 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import harmonics from '../index'
|
|
2
|
+
import mockHarmonicConstituents from '../../__mocks__/constituents'
|
|
3
|
+
import mockSecondaryStation from '../../__mocks__/secondary-station'
|
|
4
|
+
|
|
5
|
+
const startDate = new Date()
|
|
6
|
+
startDate.setFullYear(2019)
|
|
7
|
+
startDate.setMonth(8)
|
|
8
|
+
startDate.setDate(1)
|
|
9
|
+
startDate.setHours(0)
|
|
10
|
+
startDate.setMinutes(0)
|
|
11
|
+
startDate.setSeconds(0)
|
|
12
|
+
startDate.setMilliseconds(0)
|
|
13
|
+
|
|
14
|
+
const endDate = new Date()
|
|
15
|
+
endDate.setFullYear(2019)
|
|
16
|
+
endDate.setMonth(8)
|
|
17
|
+
endDate.setDate(1)
|
|
18
|
+
endDate.setHours(6)
|
|
19
|
+
endDate.setMinutes(0)
|
|
20
|
+
endDate.setSeconds(0)
|
|
21
|
+
endDate.setMilliseconds(0)
|
|
22
|
+
|
|
23
|
+
const extremesEndDate = new Date()
|
|
24
|
+
extremesEndDate.setFullYear(2019)
|
|
25
|
+
extremesEndDate.setMonth(8)
|
|
26
|
+
extremesEndDate.setDate(3)
|
|
27
|
+
extremesEndDate.setHours(0)
|
|
28
|
+
extremesEndDate.setMinutes(0)
|
|
29
|
+
extremesEndDate.setSeconds(0)
|
|
30
|
+
extremesEndDate.setMilliseconds(0)
|
|
31
|
+
|
|
32
|
+
const setUpPrediction = () => {
|
|
33
|
+
const harmonic = harmonics({
|
|
34
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
35
|
+
phaseKey: 'phase_GMT',
|
|
36
|
+
offset: false,
|
|
37
|
+
})
|
|
38
|
+
harmonic.setTimeSpan(startDate, endDate)
|
|
39
|
+
return harmonic.prediction()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('harmonic prediction', () => {
|
|
43
|
+
test('it creates a timeline prediction', () => {
|
|
44
|
+
const testPrediction = setUpPrediction()
|
|
45
|
+
const results = testPrediction.getTimelinePrediction()
|
|
46
|
+
const lastResult = results.pop()
|
|
47
|
+
expect(results[0].level).toBeCloseTo(-1.347125, 3)
|
|
48
|
+
expect(lastResult.level).toBeCloseTo(2.85263589, 3)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('it creates a timeline prediction with a non-default phase key', () => {
|
|
52
|
+
const results = harmonics({
|
|
53
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
54
|
+
phaseKey: 'phase_local',
|
|
55
|
+
offset: false,
|
|
56
|
+
})
|
|
57
|
+
.setTimeSpan(startDate, endDate)
|
|
58
|
+
.prediction()
|
|
59
|
+
.getTimelinePrediction()
|
|
60
|
+
expect(results[0].level).toBeCloseTo(2.7560979, 3)
|
|
61
|
+
expect(results.pop().level).toBeCloseTo(-2.9170977, 3)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('it finds high and low tides', () => {
|
|
65
|
+
const results = harmonics({
|
|
66
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
67
|
+
phaseKey: 'phase_GMT',
|
|
68
|
+
offset: false,
|
|
69
|
+
})
|
|
70
|
+
.setTimeSpan(startDate, extremesEndDate)
|
|
71
|
+
.prediction()
|
|
72
|
+
.getExtremesPrediction()
|
|
73
|
+
expect(results[0].level).toBeCloseTo(-1.5650332, 4)
|
|
74
|
+
|
|
75
|
+
const customLabels = {
|
|
76
|
+
high: 'Super high',
|
|
77
|
+
low: 'Wayyy low',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const labelResults = harmonics({
|
|
81
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
82
|
+
phaseKey: 'phase_GMT',
|
|
83
|
+
offset: false,
|
|
84
|
+
})
|
|
85
|
+
.setTimeSpan(startDate, extremesEndDate)
|
|
86
|
+
.prediction()
|
|
87
|
+
.getExtremesPrediction({ labels: customLabels })
|
|
88
|
+
expect(labelResults[0].label).toBe(customLabels.low)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('it finds high and low tides with high fidelity', () => {
|
|
92
|
+
const results = harmonics({
|
|
93
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
94
|
+
phaseKey: 'phase_GMT',
|
|
95
|
+
offset: false,
|
|
96
|
+
})
|
|
97
|
+
.setTimeSpan(startDate, extremesEndDate)
|
|
98
|
+
.prediction({ timeFidelity: 60 })
|
|
99
|
+
.getExtremesPrediction()
|
|
100
|
+
expect(results[0].level).toBeCloseTo(-1.5653894, 4)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('Secondary stations', () => {
|
|
105
|
+
test('it can add offsets to secondary stations', () => {
|
|
106
|
+
const regularResults = harmonics({
|
|
107
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
108
|
+
phaseKey: 'phase_GMT',
|
|
109
|
+
offset: false,
|
|
110
|
+
})
|
|
111
|
+
.setTimeSpan(startDate, extremesEndDate)
|
|
112
|
+
.prediction()
|
|
113
|
+
.getExtremesPrediction()
|
|
114
|
+
|
|
115
|
+
const offsetResults = harmonics({
|
|
116
|
+
harmonicConstituents: mockHarmonicConstituents,
|
|
117
|
+
phaseKey: 'phase_GMT',
|
|
118
|
+
offset: false,
|
|
119
|
+
})
|
|
120
|
+
.setTimeSpan(startDate, extremesEndDate)
|
|
121
|
+
.prediction()
|
|
122
|
+
.getExtremesPrediction({ offsets: mockSecondaryStation })
|
|
123
|
+
|
|
124
|
+
offsetResults.forEach((offsetResult, index) => {
|
|
125
|
+
if (offsetResult.low) {
|
|
126
|
+
expect(offsetResult.level).toBeCloseTo(
|
|
127
|
+
regularResults[index].level * mockSecondaryStation.height_offset.low,
|
|
128
|
+
4
|
|
129
|
+
)
|
|
130
|
+
expect(offsetResult.time.getTime()).toBe(
|
|
131
|
+
regularResults[index].time.getTime() +
|
|
132
|
+
mockSecondaryStation.time_offset.low * 60 * 1000
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
if (offsetResult.high) {
|
|
136
|
+
expect(offsetResult.level).toBeCloseTo(
|
|
137
|
+
regularResults[index].level * mockSecondaryStation.height_offset.high,
|
|
138
|
+
4
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
expect(offsetResult.time.getTime()).toBe(
|
|
142
|
+
regularResults[index].time.getTime() +
|
|
143
|
+
mockSecondaryStation.time_offset.high * 60 * 1000
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import prediction from './prediction'
|
|
2
|
+
import constituentModels from '../constituents/index'
|
|
3
|
+
import { d2r } from '../astronomy/constants'
|
|
4
|
+
|
|
5
|
+
const getDate = (time) => {
|
|
6
|
+
if (time instanceof Date) {
|
|
7
|
+
return time
|
|
8
|
+
}
|
|
9
|
+
if (typeof time === 'number') {
|
|
10
|
+
return new Date(time * 1000)
|
|
11
|
+
}
|
|
12
|
+
throw new Error('Invalid date format, should be a Date object, or timestamp')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const getTimeline = (start, end, seconds) => {
|
|
16
|
+
seconds = typeof seconds !== 'undefined' ? seconds : 10 * 60
|
|
17
|
+
const timeline = []
|
|
18
|
+
const endTime = end.getTime() / 1000
|
|
19
|
+
let lastTime = start.getTime() / 1000
|
|
20
|
+
const startTime = lastTime
|
|
21
|
+
const hours = []
|
|
22
|
+
while (lastTime <= endTime) {
|
|
23
|
+
timeline.push(new Date(lastTime * 1000))
|
|
24
|
+
hours.push((lastTime - startTime) / (60 * 60))
|
|
25
|
+
lastTime += seconds
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
items: timeline,
|
|
30
|
+
hours: hours,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const harmonicsFactory = ({ harmonicConstituents, phaseKey, offset }) => {
|
|
35
|
+
if (!Array.isArray(harmonicConstituents)) {
|
|
36
|
+
throw new Error('Harmonic constituents are not an array')
|
|
37
|
+
}
|
|
38
|
+
const constituents = []
|
|
39
|
+
harmonicConstituents.forEach((constituent, index) => {
|
|
40
|
+
if (typeof constituent.name === 'undefined') {
|
|
41
|
+
throw new Error('Harmonic constituents must have a name property')
|
|
42
|
+
}
|
|
43
|
+
if (typeof constituentModels[constituent.name] !== 'undefined') {
|
|
44
|
+
constituent._model = constituentModels[constituent.name]
|
|
45
|
+
constituent._phase = d2r * constituent[phaseKey]
|
|
46
|
+
constituents.push(constituent)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
if (offset !== false) {
|
|
51
|
+
constituents.push({
|
|
52
|
+
name: 'Z0',
|
|
53
|
+
_model: constituentModels.Z0,
|
|
54
|
+
_phase: 0,
|
|
55
|
+
amplitude: offset,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let start = new Date()
|
|
60
|
+
let end = new Date()
|
|
61
|
+
|
|
62
|
+
const harmonics = {}
|
|
63
|
+
|
|
64
|
+
harmonics.setTimeSpan = (startTime, endTime) => {
|
|
65
|
+
start = getDate(startTime)
|
|
66
|
+
end = getDate(endTime)
|
|
67
|
+
if (start.getTime() >= end.getTime()) {
|
|
68
|
+
throw new Error('Start time must be before end time')
|
|
69
|
+
}
|
|
70
|
+
return harmonics
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
harmonics.prediction = (options) => {
|
|
74
|
+
options =
|
|
75
|
+
typeof options !== 'undefined' ? options : { timeFidelity: 10 * 60 }
|
|
76
|
+
return prediction({
|
|
77
|
+
timeline: getTimeline(start, end, options.timeFidelity),
|
|
78
|
+
constituents: constituents,
|
|
79
|
+
start: start,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Object.freeze(harmonics)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default harmonicsFactory
|
|
87
|
+
export { getDate, getTimeline }
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import astro from '../astronomy/index'
|
|
2
|
+
import { d2r } from '../astronomy/constants'
|
|
3
|
+
|
|
4
|
+
const modulus = (a, b) => {
|
|
5
|
+
return ((a % b) + b) % b
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const addExtremesOffsets = (extreme, offsets) => {
|
|
9
|
+
if (typeof offsets === 'undefined' || !offsets) {
|
|
10
|
+
return extreme
|
|
11
|
+
}
|
|
12
|
+
if (extreme.high && offsets.height_offset && offsets.height_offset.high) {
|
|
13
|
+
extreme.level *= offsets.height_offset.high
|
|
14
|
+
}
|
|
15
|
+
if (extreme.low && offsets.height_offset && offsets.height_offset.low) {
|
|
16
|
+
extreme.level *= offsets.height_offset.low
|
|
17
|
+
}
|
|
18
|
+
if (extreme.high && offsets.time_offset && offsets.time_offset.high) {
|
|
19
|
+
extreme.time = new Date(
|
|
20
|
+
extreme.time.getTime() + offsets.time_offset.high * 60 * 1000
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
if (extreme.low && offsets.time_offset && offsets.time_offset.low) {
|
|
24
|
+
extreme.time = new Date(
|
|
25
|
+
extreme.time.getTime() + offsets.time_offset.low * 60 * 1000
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
return extreme
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getExtremeLabel = (label, highLowLabels) => {
|
|
32
|
+
if (
|
|
33
|
+
typeof highLowLabels !== 'undefined' &&
|
|
34
|
+
typeof highLowLabels[label] !== 'undefined'
|
|
35
|
+
) {
|
|
36
|
+
return highLowLabels[label]
|
|
37
|
+
}
|
|
38
|
+
const labels = {
|
|
39
|
+
high: 'High',
|
|
40
|
+
low: 'Low',
|
|
41
|
+
}
|
|
42
|
+
return labels[label]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const predictionFactory = ({ timeline, constituents, start }) => {
|
|
46
|
+
const getLevel = (hour, modelBaseSpeed, modelU, modelF, modelBaseValue) => {
|
|
47
|
+
const amplitudes = []
|
|
48
|
+
let result = 0
|
|
49
|
+
|
|
50
|
+
constituents.forEach((constituent) => {
|
|
51
|
+
const amplitude = constituent.amplitude
|
|
52
|
+
const phase = constituent._phase
|
|
53
|
+
const f = modelF[constituent.name]
|
|
54
|
+
const speed = modelBaseSpeed[constituent.name]
|
|
55
|
+
const u = modelU[constituent.name]
|
|
56
|
+
const V0 = modelBaseValue[constituent.name]
|
|
57
|
+
amplitudes.push(amplitude * f * Math.cos(speed * hour + (V0 + u) - phase))
|
|
58
|
+
})
|
|
59
|
+
// sum up each row
|
|
60
|
+
amplitudes.forEach((item) => {
|
|
61
|
+
result += item
|
|
62
|
+
})
|
|
63
|
+
return result
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const prediction = {}
|
|
67
|
+
|
|
68
|
+
prediction.getExtremesPrediction = (options) => {
|
|
69
|
+
const { labels, offsets } = typeof options !== 'undefined' ? options : {}
|
|
70
|
+
const results = []
|
|
71
|
+
const { baseSpeed, u, f, baseValue } = prepare()
|
|
72
|
+
let goingUp = false
|
|
73
|
+
let goingDown = false
|
|
74
|
+
let lastLevel = getLevel(0, baseSpeed, u[0], f[0], baseValue)
|
|
75
|
+
timeline.items.forEach((time, index) => {
|
|
76
|
+
const hour = timeline.hours[index]
|
|
77
|
+
const level = getLevel(hour, baseSpeed, u[index], f[index], baseValue)
|
|
78
|
+
// Compare this level to the last one, if we
|
|
79
|
+
// are changing angle, then the last one was high or low
|
|
80
|
+
if (level > lastLevel && goingDown) {
|
|
81
|
+
results.push(
|
|
82
|
+
addExtremesOffsets(
|
|
83
|
+
{
|
|
84
|
+
time: timeline.items[index - 1],
|
|
85
|
+
level: lastLevel,
|
|
86
|
+
high: false,
|
|
87
|
+
low: true,
|
|
88
|
+
label: getExtremeLabel('low', labels),
|
|
89
|
+
},
|
|
90
|
+
offsets
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
if (level < lastLevel && goingUp) {
|
|
95
|
+
results.push(
|
|
96
|
+
addExtremesOffsets(
|
|
97
|
+
{
|
|
98
|
+
time: timeline.items[index - 1],
|
|
99
|
+
level: lastLevel,
|
|
100
|
+
high: true,
|
|
101
|
+
low: false,
|
|
102
|
+
label: getExtremeLabel('high', labels),
|
|
103
|
+
},
|
|
104
|
+
offsets
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (level > lastLevel) {
|
|
109
|
+
goingUp = true
|
|
110
|
+
goingDown = false
|
|
111
|
+
}
|
|
112
|
+
if (level < lastLevel) {
|
|
113
|
+
goingUp = false
|
|
114
|
+
goingDown = true
|
|
115
|
+
}
|
|
116
|
+
lastLevel = level
|
|
117
|
+
})
|
|
118
|
+
return results
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
prediction.getTimelinePrediction = () => {
|
|
122
|
+
const results = []
|
|
123
|
+
const { baseSpeed, u, f, baseValue } = prepare()
|
|
124
|
+
timeline.items.forEach((time, index) => {
|
|
125
|
+
const hour = timeline.hours[index]
|
|
126
|
+
const prediction = {
|
|
127
|
+
time: time,
|
|
128
|
+
hour: hour,
|
|
129
|
+
level: getLevel(hour, baseSpeed, u[index], f[index], baseValue),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
results.push(prediction)
|
|
133
|
+
})
|
|
134
|
+
return results
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const prepare = () => {
|
|
138
|
+
const baseAstro = astro(start)
|
|
139
|
+
|
|
140
|
+
const baseValue = {}
|
|
141
|
+
const baseSpeed = {}
|
|
142
|
+
const u = []
|
|
143
|
+
const f = []
|
|
144
|
+
constituents.forEach((constituent) => {
|
|
145
|
+
const value = constituent._model.value(baseAstro)
|
|
146
|
+
const speed = constituent._model.speed(baseAstro)
|
|
147
|
+
baseValue[constituent.name] = d2r * value
|
|
148
|
+
baseSpeed[constituent.name] = d2r * speed
|
|
149
|
+
})
|
|
150
|
+
timeline.items.forEach((time) => {
|
|
151
|
+
const uItem = {}
|
|
152
|
+
const fItem = {}
|
|
153
|
+
const itemAstro = astro(time)
|
|
154
|
+
constituents.forEach((constituent) => {
|
|
155
|
+
const constituentU = modulus(constituent._model.u(itemAstro), 360)
|
|
156
|
+
|
|
157
|
+
uItem[constituent.name] = d2r * constituentU
|
|
158
|
+
fItem[constituent.name] = modulus(constituent._model.f(itemAstro), 360)
|
|
159
|
+
})
|
|
160
|
+
u.push(uItem)
|
|
161
|
+
f.push(fItem)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
baseValue: baseValue,
|
|
166
|
+
baseSpeed: baseSpeed,
|
|
167
|
+
u: u,
|
|
168
|
+
f: f,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return Object.freeze(prediction)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default predictionFactory
|
package/src/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import harmonics from './harmonics/index'
|
|
2
|
+
|
|
3
|
+
const tidePredictionFactory = (constituents, options) => {
|
|
4
|
+
const harmonicsOptions = {
|
|
5
|
+
harmonicConstituents: constituents,
|
|
6
|
+
phaseKey: 'phase_GMT',
|
|
7
|
+
offset: false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (typeof options !== 'undefined') {
|
|
11
|
+
Object.keys(harmonicsOptions).forEach(key => {
|
|
12
|
+
if (typeof options[key] !== 'undefined') {
|
|
13
|
+
harmonicsOptions[key] = options[key]
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const tidePrediction = {
|
|
19
|
+
getTimelinePrediction: ({ start, end }) => {
|
|
20
|
+
return harmonics(harmonicsOptions)
|
|
21
|
+
.setTimeSpan(start, end)
|
|
22
|
+
.prediction()
|
|
23
|
+
.getTimelinePrediction()
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
getExtremesPrediction: ({ start, end, labels, offsets, timeFidelity }) => {
|
|
27
|
+
return harmonics(harmonicsOptions)
|
|
28
|
+
.setTimeSpan(start, end)
|
|
29
|
+
.prediction({ timeFidelity: timeFidelity })
|
|
30
|
+
.getExtremesPrediction(labels, offsets)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
getWaterLevelAtTime: ({ time }) => {
|
|
34
|
+
const endDate = new Date(time.getTime() + 10 * 60 * 1000)
|
|
35
|
+
return harmonics(harmonicsOptions)
|
|
36
|
+
.setTimeSpan(time, endDate)
|
|
37
|
+
.prediction()
|
|
38
|
+
.getTimelinePrediction()[0]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return tidePrediction
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default tidePredictionFactory
|