@idm-plugin/meteo2 0.0.1
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/README.md +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +405 -0
- package/dist/index.umd.cjs +1 -0
- package/dist/openmeteo/src/index.d.ts +70 -0
- package/dist/read/src/index.d.ts +165 -0
- package/package.json +70 -0
package/README.md
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
var T = Object.defineProperty;
|
|
2
|
+
var F = (_, e, s) => e in _ ? T(_, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : _[e] = s;
|
|
3
|
+
var y = (_, e, s) => (F(_, typeof e != "symbol" ? e + "" : e, s), s);
|
|
4
|
+
import D from "@log4js-node/log4js-api";
|
|
5
|
+
import f from "moment";
|
|
6
|
+
import I from "got";
|
|
7
|
+
import { fetchWeatherApi as O } from "openmeteo";
|
|
8
|
+
let u;
|
|
9
|
+
try {
|
|
10
|
+
u = D.getLogger("meteo");
|
|
11
|
+
} catch {
|
|
12
|
+
} finally {
|
|
13
|
+
}
|
|
14
|
+
var U = /* @__PURE__ */ ((_) => (_.Arome = "arome", _.IconEU = "iconEu", _.GFS = "gfs", _.GFSWave = "gfsWave", _.NamConus = "namConus", _.NamHawaii = "namHawaii", _.NamAlaska = "namAlaska", _.Geos5 = "geos5", _))(U || {});
|
|
15
|
+
class W {
|
|
16
|
+
/**
|
|
17
|
+
* 点查海洋气象要素(全量)
|
|
18
|
+
* @param lng
|
|
19
|
+
* @param lat
|
|
20
|
+
* @param timestamp 单位毫秒
|
|
21
|
+
* @param wt 是否查海温
|
|
22
|
+
* @param source
|
|
23
|
+
* @param options
|
|
24
|
+
*/
|
|
25
|
+
static async queryPointMeteo(e, s, t, o = !1, a = "", l = {}) {
|
|
26
|
+
typeof t == "number" && (t = t < 1e12 ? t * 1e3 : t);
|
|
27
|
+
const c = f(t), r = {
|
|
28
|
+
searchParams: {
|
|
29
|
+
lng: e,
|
|
30
|
+
lat: s,
|
|
31
|
+
ts: c.valueOf(),
|
|
32
|
+
params: o ? "watertemp" : void 0,
|
|
33
|
+
source: a == null ? void 0 : a.toLowerCase()
|
|
34
|
+
},
|
|
35
|
+
timeout: 3e4
|
|
36
|
+
}, h = f(), n = h.valueOf();
|
|
37
|
+
c.isBefore(h.subtract(1, "month")) && (u == null || u.warn("[%s] get history meteo on %s: %j", l.requestId, c.format(), r));
|
|
38
|
+
const w = "https://aod4idm.idmwx.com/api/ocean/point", i = await I.get(w, r).json(), d = f().valueOf();
|
|
39
|
+
if (u == null || u.info("[%s] get meteo(cost: %d ms) from %s with options: %j", l.requestId, d - n, w, r), (i == null ? void 0 : i.code) === 0)
|
|
40
|
+
return {
|
|
41
|
+
...i.data,
|
|
42
|
+
source: a
|
|
43
|
+
};
|
|
44
|
+
u == null || u.warn("[%s] get meteo failed: %j", l.requestId, i);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 点查海洋气象要素(指定要素组合)
|
|
48
|
+
* @param lng
|
|
49
|
+
* @param lat
|
|
50
|
+
* @param timestamp 单位毫秒
|
|
51
|
+
* @param params 要查询的要素组合
|
|
52
|
+
* 1) wind, temp, prmsl, precip 来自于同一预报文件, 查wind同时也带出其它要素, 支持gfs和cmems
|
|
53
|
+
* 2) wave 支持gfs和cmems
|
|
54
|
+
* 3) current 来自smoc, 不区分gfs和cmems
|
|
55
|
+
* 4) watertemp 来自cmems, 不区分gfs和cmems
|
|
56
|
+
* 5)visibility gusts cloud weather dp_temp 仅支持gfs
|
|
57
|
+
* @param source
|
|
58
|
+
* @param options
|
|
59
|
+
*/
|
|
60
|
+
static async queryPointFactor(e, s, t, o = "wind,wave,current,watertemp,visibility", a = "", l = {}) {
|
|
61
|
+
typeof t == "number" && (t = t < 1e12 ? t * 1e3 : t);
|
|
62
|
+
const c = f(t), r = {
|
|
63
|
+
searchParams: {
|
|
64
|
+
lng: e,
|
|
65
|
+
lat: s,
|
|
66
|
+
ts: c.valueOf(),
|
|
67
|
+
params: o,
|
|
68
|
+
source: a == null ? void 0 : a.toLowerCase()
|
|
69
|
+
},
|
|
70
|
+
timeout: 3e4
|
|
71
|
+
}, h = f(), n = h.valueOf();
|
|
72
|
+
c.isBefore(h.subtract(1, "month")) && (u == null || u.warn("[%s] get history factors on %s: %j", l.requestId, c.format(), r));
|
|
73
|
+
const w = "https://aod4idm.idmwx.com/api/ocean/factor", i = await I.get(w, r).json(), d = f().valueOf();
|
|
74
|
+
if (u == null || u.info("[%s] get factors(cost: %d ms) from %s with options: %j", l.requestId, d - n, w, r), (i == null ? void 0 : i.code) === 0)
|
|
75
|
+
return {
|
|
76
|
+
...i.data,
|
|
77
|
+
source: a
|
|
78
|
+
};
|
|
79
|
+
u == null || u.warn("[%s] get factors failed: %j", l.requestId, i);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* @see https://api.windy.com/point-forecast/docs
|
|
83
|
+
* @param key
|
|
84
|
+
*/
|
|
85
|
+
static async queryWindyPointForecast(e, s, t, o = {}) {
|
|
86
|
+
const a = "https://api.windy.com/api/point-forecast/v2", l = [];
|
|
87
|
+
try {
|
|
88
|
+
let c = f().valueOf();
|
|
89
|
+
const r = await I.post(a, {
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/json"
|
|
92
|
+
},
|
|
93
|
+
json: {
|
|
94
|
+
lat: e,
|
|
95
|
+
lon: s,
|
|
96
|
+
key: t,
|
|
97
|
+
model: "gfs",
|
|
98
|
+
parameters: [
|
|
99
|
+
"temp",
|
|
100
|
+
"dewpoint",
|
|
101
|
+
"precip",
|
|
102
|
+
"convPrecip",
|
|
103
|
+
"snowPrecip",
|
|
104
|
+
"wind",
|
|
105
|
+
"windGust",
|
|
106
|
+
"cape",
|
|
107
|
+
"ptype",
|
|
108
|
+
"lclouds",
|
|
109
|
+
"mclouds",
|
|
110
|
+
"hclouds",
|
|
111
|
+
"rh",
|
|
112
|
+
"gh",
|
|
113
|
+
"pressure"
|
|
114
|
+
],
|
|
115
|
+
levels: ["surface"]
|
|
116
|
+
}
|
|
117
|
+
}).json();
|
|
118
|
+
let h = f().valueOf();
|
|
119
|
+
u == null || u.info("[%s] get gfs-factors(cost: %d ms) from %s", o.requestId, h - c, a), c = h;
|
|
120
|
+
const n = await I.post(a, {
|
|
121
|
+
headers: {
|
|
122
|
+
"Content-Type": "application/json"
|
|
123
|
+
},
|
|
124
|
+
json: {
|
|
125
|
+
lat: e,
|
|
126
|
+
lon: s,
|
|
127
|
+
key: t,
|
|
128
|
+
model: "gfsWave",
|
|
129
|
+
parameters: ["waves", "windWaves", "swell1", "swell2"],
|
|
130
|
+
levels: ["surface"]
|
|
131
|
+
}
|
|
132
|
+
}).json();
|
|
133
|
+
h = f().valueOf(), u == null || u.info("[%s] get gfs-wave-factors(cost: %d ms) from %s", o.requestId, h - c, a);
|
|
134
|
+
for (let i = 0; i < r.ts.length; i++) {
|
|
135
|
+
const d = this.populateUVFactor(r["wind_u-surface"][i], r["wind_v-surface"][i], !1, o);
|
|
136
|
+
d.scale = this.calculateBeaufortWindForceScale(d.speed), l.push({
|
|
137
|
+
utc: f(r.ts[i]).utc().format(),
|
|
138
|
+
temp: r["temp-surface"][i] ? Math.round((r["temp-surface"][i] - 273.15) * 100) / 100 : void 0,
|
|
139
|
+
dp_temp: r["dewpoint-surface"][i] ? Math.round((r["dewpoint-surface"][i] - 273.15) * 100) / 100 : void 0,
|
|
140
|
+
precip: {
|
|
141
|
+
inter3h: r["past3hprecip-surface"][i] ? Math.round(r["past3hprecip-surface"][i] * 1e3 * 1e3) / 1e3 : 0,
|
|
142
|
+
inter3hSnow: r["past3hsnowprecip-surface"][i] ? Math.round(r["past3hsnowprecip-surface"][i] * 1e3 * 1e3) / 1e3 : 0,
|
|
143
|
+
inter3hConv: r["past3hconvprecip-surface"][i] ? Math.round(r["past3hconvprecip-surface"][i] * 1e3 * 1e3) / 1e3 : 0
|
|
144
|
+
},
|
|
145
|
+
wind: d,
|
|
146
|
+
gusts: {
|
|
147
|
+
speed: Math.round((r["gust-surface"][i] || 0) * 100) / 100,
|
|
148
|
+
kts: this.convertMs2Kts(r["gust-surface"][i])
|
|
149
|
+
},
|
|
150
|
+
lclouds: r["lclouds-surface"][i] ? Math.round(r["lclouds-surface"][i] * 100) / 100 : 0,
|
|
151
|
+
mclouds: r["mclouds-surface"][i] ? Math.round(r["mclouds-surface"][i] * 100) / 100 : 0,
|
|
152
|
+
hclouds: r["hclouds-surface"][i] ? Math.round(r["hclouds-surface"][i] * 100) / 100 : 0,
|
|
153
|
+
rh: r["rh-surface"][i] ? Math.round(r["rh-surface"][i] * 100) / 100 : 0,
|
|
154
|
+
gh: r["gh-surface"][i] ? Math.round(r["gh-surface"][i] * 100) / 100 : 0,
|
|
155
|
+
pressure: Math.round(r["pressure-surface"][i] / 100 * 100) / 100
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const w = [];
|
|
159
|
+
for (let i = 0; i < n.ts.length; i++) {
|
|
160
|
+
const d = this.calculateDouglasScale(n["waves_height-surface"][i], n["waves_direction-surface"][i], n["waves_period-surface"][i]), R = this.calculateDouglasScale(n["wwaves_height-surface"][i], n["wwaves_direction-surface"][i], n["wwaves_period-surface"][i]), p = this.calculateDouglasScale(n["swell1_height-surface"][i], n["swell1_direction-surface"][i], n["swell1_period-surface"][i]), E = this.calculateDouglasScale(n["swell2_height-surface"][i], n["swell2_direction-surface"][i], n["swell2_period-surface"][i]);
|
|
161
|
+
w.push({
|
|
162
|
+
utc: f(n.ts[i]).utc().format(),
|
|
163
|
+
wave: {
|
|
164
|
+
sig: d,
|
|
165
|
+
wd: R,
|
|
166
|
+
swell: p,
|
|
167
|
+
swell2: E
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
for (const i of l) {
|
|
172
|
+
const d = w.find((R) => R.utc === i.utc);
|
|
173
|
+
i.wave = d == null ? void 0 : d.wave;
|
|
174
|
+
}
|
|
175
|
+
} catch (c) {
|
|
176
|
+
u.warn("[%s] get-gfs-factor failed: %s", o.requestId, c);
|
|
177
|
+
}
|
|
178
|
+
return l;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 填充UV向量
|
|
182
|
+
* @param eastward m/s
|
|
183
|
+
* @param northward m/s
|
|
184
|
+
* @param reverse 反转, 如计算洋流方向时,风来流去
|
|
185
|
+
* @param options
|
|
186
|
+
*/
|
|
187
|
+
static populateUVFactor(e, s, t = !1, o = {}) {
|
|
188
|
+
const a = Math.round(Math.sqrt(Math.pow(e, 2) + Math.pow(s, 2)) * 1e4) / 1e4, { degree: l, direction: c } = this.calculateUVDirection(e, s, t, o), r = this.convertMs2Kts(a);
|
|
189
|
+
return {
|
|
190
|
+
speed: a,
|
|
191
|
+
kts: r,
|
|
192
|
+
degree: l,
|
|
193
|
+
direction: c,
|
|
194
|
+
eastward: Math.round(e * 1e4) / 1e4,
|
|
195
|
+
northward: Math.round(s * 1e4) / 1e4
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
static convertMs2Kts(e) {
|
|
199
|
+
return isNaN(e) ? 0 : Math.round(e * 3600 / 1852 * 1e3) / 1e3;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 计算UV向量方向
|
|
203
|
+
* @param eastward m/s
|
|
204
|
+
* @param northward m/s
|
|
205
|
+
* @param reverse 反转, 如计算洋流方向时,风来流去
|
|
206
|
+
* @param options
|
|
207
|
+
*/
|
|
208
|
+
static calculateUVDirection(e, s, t = !1, o = {}) {
|
|
209
|
+
let a = Math.atan2(e, s) + Math.PI;
|
|
210
|
+
t && (a = Math.atan2(e, s));
|
|
211
|
+
const l = this.convert2Direction(a);
|
|
212
|
+
return a = Math.round(a / (2 * Math.PI) * 360 * 1e4) / 1e4, {
|
|
213
|
+
angle: a,
|
|
214
|
+
degree: a,
|
|
215
|
+
direction: l
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 将弧度转换为方向
|
|
220
|
+
* @param delta 弧度
|
|
221
|
+
* @return {string}
|
|
222
|
+
*/
|
|
223
|
+
static convert2Direction(e) {
|
|
224
|
+
let s = "N/A";
|
|
225
|
+
if (!isNaN(e)) {
|
|
226
|
+
e < 0 && (e += 2 * Math.PI);
|
|
227
|
+
const t = Math.PI / 16;
|
|
228
|
+
e < t ? s = "N" : e >= t && e < 3 * t ? s = "NNE" : e >= 3 * t && e < 5 * t ? s = "NE" : e >= 5 * t && e < 7 * t ? s = "ENE" : e >= 7 * t && e < 9 * t ? s = "E" : e >= 9 * t && e < 11 * t ? s = "ESE" : e >= 11 * t && e < 13 * t ? s = "SE" : e >= 13 * t && e < 15 * t ? s = "SSE" : e >= 15 * t && e < 17 * t ? s = "S" : e >= 17 * t && e < 19 * t ? s = "SSW" : e >= 19 * t && e < 21 * t ? s = "SW" : e >= 21 * t && e < 23 * t ? s = "WSW" : e >= 23 * t && e < 25 * t ? s = "W" : e >= 25 * t && e < 27 * t ? s = "WNW" : e >= 27 * t && e < 29 * t ? s = "NW" : e >= 29 * t && e < 31 * t ? s = "NNW" : e >= 31 * t && e < 32 * t && (s = "N");
|
|
229
|
+
}
|
|
230
|
+
return s;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 计算风力蒲福等级
|
|
234
|
+
* @param speed m/s
|
|
235
|
+
* @returns {number}
|
|
236
|
+
*/
|
|
237
|
+
static calculateBeaufortWindForceScale(e) {
|
|
238
|
+
let s = 0;
|
|
239
|
+
return isNaN(e) || (e = Math.round(e * 10) / 10, e <= 0.2 ? s = 0 : e <= 1.5 ? s = 1 : e <= 3.3 ? s = 2 : e <= 5.4 ? s = 3 : e <= 7.9 ? s = 4 : e <= 10.7 ? s = 5 : e <= 13.8 ? s = 6 : e <= 17.1 ? s = 7 : e <= 20.7 ? s = 8 : e <= 22.4 ? s = 9 : e <= 28.4 ? s = 10 : e <= 32.6 ? s = 11 : e > 32.6 && (s = 12)), s;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 计算海浪等级
|
|
243
|
+
* @param height 高度 m
|
|
244
|
+
* @param degree 度
|
|
245
|
+
* @param period 周期 s
|
|
246
|
+
*/
|
|
247
|
+
static calculateDouglasScale(e, s, t) {
|
|
248
|
+
let o = "Calm";
|
|
249
|
+
const a = this.convert2Direction(s / 360 * 2 * Math.PI);
|
|
250
|
+
return isNaN(e) || e <= 0.1 ? o = "Calm" : e <= 0.5 ? o = "Smooth" : e <= 1.25 ? o = "Slight" : e <= 2.5 ? o = "Moderate" : e <= 4 ? o = "Rough" : e <= 6 ? o = "VeryRough" : e <= 9 ? o = "High" : e <= 14 ? o = "VeryHigh" : o = "Precipitous", {
|
|
251
|
+
degree: Math.round(s * 100) / 100,
|
|
252
|
+
scale: o,
|
|
253
|
+
direction: a,
|
|
254
|
+
height: Math.round(e * 1e3) / 1e3,
|
|
255
|
+
period: Math.round(t * 100) / 100
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let S;
|
|
260
|
+
try {
|
|
261
|
+
S = D.getLogger("open-meteo");
|
|
262
|
+
} catch {
|
|
263
|
+
} finally {
|
|
264
|
+
}
|
|
265
|
+
class k {
|
|
266
|
+
constructor(e, s) {
|
|
267
|
+
y(this, "apikey");
|
|
268
|
+
y(this, "debug");
|
|
269
|
+
// private readonly FORECAST_URL: string = 'https://api.open-meteo.com/v1/forecast'
|
|
270
|
+
// private readonly HISTORY_FORECAST_URL: string = 'https://historical-forecast-api.open-meteo.com/v1/forecast'
|
|
271
|
+
// private readonly MARINE_FORECAST_URL: string = 'https://marine-api.open-meteo.com/v1/marine'
|
|
272
|
+
y(this, "FORECAST_URL", "https://customer-api.open-meteo.com/v1/forecast");
|
|
273
|
+
y(this, "SELF_FORECAST_URL", "https://meteo2.idmwx.com/v1/forecast");
|
|
274
|
+
y(this, "HISTORY_FORECAST_URL", "https://customer-historical-forecast-api.open-meteo.com/v1/forecast");
|
|
275
|
+
y(this, "MARINE_FORECAST_URL", "https://customer-marine-api.open-meteo.com/v1/marine");
|
|
276
|
+
y(this, "SELF_MARINE_FORECAST_URL", "https://meteo2.idmwx.com/v1/marine");
|
|
277
|
+
y(this, "WEATHER_VARIABLES", {
|
|
278
|
+
NORMAL: {
|
|
279
|
+
DAILY: "weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,precipitation_sum,precipitation_hours,precipitation_probability_max,wind_speed_10m_max,wind_gusts_10m_max,wind_direction_10m_dominant",
|
|
280
|
+
HOURLY: "temperature_2m,relative_humidity_2m,dew_point_2m,apparent_temperature,precipitation_probability,precipitation,weather_code,visibility,wind_speed_10m,wind_direction_10m,wind_gusts_10m",
|
|
281
|
+
CURRENT: "temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m"
|
|
282
|
+
},
|
|
283
|
+
SIMPLE: {
|
|
284
|
+
DAILY: "weather_code",
|
|
285
|
+
HOURLY: "wind_speed_10m,wind_direction_10m,wind_gusts_10m",
|
|
286
|
+
CURRENT: "wind_speed_10m,wind_direction_10m,wind_gusts_10m"
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
y(this, "MARINE_VARIABLES", {
|
|
290
|
+
DAILY: "wave_height_max,wave_direction_dominant,wave_period_max,wind_wave_height_max,wind_wave_direction_dominant,wind_wave_period_max,wind_wave_peak_period_max,swell_wave_height_max,swell_wave_direction_dominant,swell_wave_period_max,swell_wave_peak_period_max",
|
|
291
|
+
HOURLY: "wave_height,wave_direction,wave_period,wind_wave_height,wind_wave_direction,wind_wave_period,wind_wave_peak_period,swell_wave_height,swell_wave_direction,swell_wave_period,swell_wave_peak_period,ocean_current_velocity,ocean_current_direction",
|
|
292
|
+
CURRENT: "wave_height,wave_direction,wave_period,wind_wave_height,wind_wave_direction,wind_wave_period,wind_wave_peak_period,swell_wave_height,swell_wave_direction,swell_wave_period,swell_wave_peak_period,ocean_current_velocity,ocean_current_direction"
|
|
293
|
+
});
|
|
294
|
+
this.apikey = e || "smE3JnDLHy3TizVv", this.debug = s;
|
|
295
|
+
}
|
|
296
|
+
range(e, s, t) {
|
|
297
|
+
return Array.from({ length: (s - e) / t }, (o, a) => e + a * t);
|
|
298
|
+
}
|
|
299
|
+
async weatherForecast(e, s = {}) {
|
|
300
|
+
const t = f(), o = await O(e.url, e);
|
|
301
|
+
delete e.apikey;
|
|
302
|
+
const a = f();
|
|
303
|
+
return this.debug && S.info("[%s] fetch weather api (%s) cost: %d ms", s.requestId, e.url, a.diff(t, "ms")), this.parseWeatherData(o, e, s);
|
|
304
|
+
}
|
|
305
|
+
async marineForecast(e, s = {}) {
|
|
306
|
+
const t = f(), o = await O(e.url, e);
|
|
307
|
+
delete e.apikey;
|
|
308
|
+
const a = f();
|
|
309
|
+
return this.debug && S.info("[%s] fetch marine api (%s) cost: %d ms", s.requestId, e.url, a.diff(t, "ms")), this.parseWeatherData(o, e, s);
|
|
310
|
+
}
|
|
311
|
+
async parseWeatherData(e, s, t = {}) {
|
|
312
|
+
var l, c, r, h, n, w;
|
|
313
|
+
const o = [], a = Math.pow(10, t.precision || 6);
|
|
314
|
+
for (const i of e) {
|
|
315
|
+
const d = i.utcOffsetSeconds(), R = i.current(), p = i.hourly(), E = i.daily(), A = {};
|
|
316
|
+
if (R) {
|
|
317
|
+
const N = f();
|
|
318
|
+
A.current = {
|
|
319
|
+
time: f.unix(Number(R.time()) + d).utc().format()
|
|
320
|
+
};
|
|
321
|
+
for (let m = 0; m < R.variablesLength(); m++) {
|
|
322
|
+
const v = R.variables(m).value();
|
|
323
|
+
A.current[s.current[m]] = Number.isNaN(v) ? v : Math.round(v * a) / a;
|
|
324
|
+
}
|
|
325
|
+
const M = f();
|
|
326
|
+
this.debug && S.info("[%s] fetch current variables cost: %d ms", t.requestId, M.diff(N, "ms"));
|
|
327
|
+
}
|
|
328
|
+
if (p) {
|
|
329
|
+
const N = f(), M = f.unix(Number(p.time()) + d);
|
|
330
|
+
A.hourly = {
|
|
331
|
+
date: M.utc().format(),
|
|
332
|
+
time: this.range(Number(p.time()), Number(p.timeEnd()), p.interval()).map((v) => f.unix(v + d).diff(M, "h"))
|
|
333
|
+
};
|
|
334
|
+
for (let v = 0; v < p.variablesLength(); v++) {
|
|
335
|
+
const L = (r = (c = (l = p.variables(v).valuesArray()) == null ? void 0 : l.toString()) == null ? void 0 : c.split(",")) == null ? void 0 : r.map((b) => Number.isNaN(b) ? b : Math.round(Number(b) * a) / a);
|
|
336
|
+
A.hourly[s.hourly[v]] = L;
|
|
337
|
+
}
|
|
338
|
+
const m = f();
|
|
339
|
+
this.debug && S.info("[%s] fetch hourly variables cost: %d ms", t.requestId, m.diff(N, "ms"));
|
|
340
|
+
}
|
|
341
|
+
if (E) {
|
|
342
|
+
const N = f();
|
|
343
|
+
A.daily = {
|
|
344
|
+
time: this.range(Number(E.time()), Number(E.timeEnd()), E.interval()).map(
|
|
345
|
+
(m) => f.unix(m + d).utc().format()
|
|
346
|
+
)
|
|
347
|
+
};
|
|
348
|
+
for (let m = 0; m < E.variablesLength(); m++) {
|
|
349
|
+
const v = (w = (n = (h = E.variables(m).valuesArray()) == null ? void 0 : h.toString()) == null ? void 0 : n.split(",")) == null ? void 0 : w.map((L) => Number.isNaN(L) ? L : Math.round(Number(L) * a) / a);
|
|
350
|
+
A.daily[s.daily[m]] = v;
|
|
351
|
+
}
|
|
352
|
+
const M = f();
|
|
353
|
+
this.debug && S.info("[%s] fetch daily variables cost: %d ms", t.requestId, M.diff(N, "ms"));
|
|
354
|
+
}
|
|
355
|
+
o.push(A);
|
|
356
|
+
}
|
|
357
|
+
return o;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* 完善start_hour/end_hour 或者是forecast_days变量,二选一
|
|
361
|
+
* @param datetime 从指定时间开始,不指定时从当前时间开始
|
|
362
|
+
* @param params 要查询的要素
|
|
363
|
+
* @param options 请求参数
|
|
364
|
+
* @private
|
|
365
|
+
*/
|
|
366
|
+
prepare(e, s, t, o = {}) {
|
|
367
|
+
e ? (t.start_date = e.utc().set({ minute: 0, second: 0, millisecond: 0 }).format("YYYY-MM-DD"), t.end_date = e.clone().add(o.forecastDays ?? 1, "d").utc().format("YYYY-MM-DD")) : t.forecast_days = o.forecastDays;
|
|
368
|
+
const a = f().subtract(1, "d");
|
|
369
|
+
return s ? (t.url = o.selfHosted ? this.SELF_MARINE_FORECAST_URL : this.MARINE_FORECAST_URL, e != null && e.isBefore(a) && (t.url = this.MARINE_FORECAST_URL)) : (t.url = o.selfHosted ? this.SELF_FORECAST_URL : this.FORECAST_URL, e != null && e.isBefore(a) && (t.url = this.HISTORY_FORECAST_URL)), t;
|
|
370
|
+
}
|
|
371
|
+
async spotForecast(e, s, t, o = !0, a = !0, l = !1, c = {
|
|
372
|
+
forecastDays: 1,
|
|
373
|
+
precision: 6,
|
|
374
|
+
selfHosted: !0
|
|
375
|
+
}) {
|
|
376
|
+
var i, d, R, p, E, A, N, M;
|
|
377
|
+
const r = {
|
|
378
|
+
apikey: this.apikey,
|
|
379
|
+
latitude: e,
|
|
380
|
+
longitude: s,
|
|
381
|
+
cell_selection: "sea",
|
|
382
|
+
wind_speed_unit: "kn",
|
|
383
|
+
models: ((i = c.weatherModels) == null ? void 0 : i.split(",")) || ["best_match"],
|
|
384
|
+
timezone: "auto"
|
|
385
|
+
};
|
|
386
|
+
a && (r.daily = o ? ((d = this.WEATHER_VARIABLES.SIMPLE.DAILY) == null ? void 0 : d.split(",")) || [] : ((R = this.WEATHER_VARIABLES.NORMAL.DAILY) == null ? void 0 : R.split(",")) || []), t || (c.forecastDays = c.forecastDays || 1, r.current = o ? ((p = this.WEATHER_VARIABLES.SIMPLE.CURRENT) == null ? void 0 : p.split(",")) || [] : ((E = this.WEATHER_VARIABLES.NORMAL.CURRENT) == null ? void 0 : E.split(",")) || []), l && (r.hourly = o ? ((A = this.WEATHER_VARIABLES.SIMPLE.HOURLY) == null ? void 0 : A.split(",")) || [] : ((N = this.WEATHER_VARIABLES.NORMAL.HOURLY) == null ? void 0 : N.split(",")) || []), this.prepare(t, !1, r, c);
|
|
387
|
+
const h = await this.weatherForecast(r, c), n = {
|
|
388
|
+
apikey: this.apikey,
|
|
389
|
+
latitude: e,
|
|
390
|
+
longitude: s,
|
|
391
|
+
cell_selection: "sea",
|
|
392
|
+
timezone: "auto",
|
|
393
|
+
wind_speed_unit: "kn",
|
|
394
|
+
models: ((M = c.marineModels) == null ? void 0 : M.split(",")) || ["best_match"]
|
|
395
|
+
};
|
|
396
|
+
a && (n.daily = this.MARINE_VARIABLES.DAILY.split(",")), t || (c.forecastDays = c.forecastDays || 1, n.current = this.MARINE_VARIABLES.CURRENT.split(",")), l && (n.hourly = this.MARINE_VARIABLES.HOURLY.split(",")), this.prepare(t, !0, n, c);
|
|
397
|
+
const w = await this.marineForecast(n, c);
|
|
398
|
+
return { weather: h, marine: w };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
export {
|
|
402
|
+
W as MeteoHelper,
|
|
403
|
+
k as MeteoHelper2,
|
|
404
|
+
U as WindyModel
|
|
405
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(_,w){typeof exports=="object"&&typeof module<"u"?w(exports,require("@log4js-node/log4js-api"),require("moment"),require("got"),require("openmeteo")):typeof define=="function"&&define.amd?define(["exports","@log4js-node/log4js-api","moment","got","openmeteo"],w):(_=typeof globalThis<"u"?globalThis:_||self,w(_["idm-plugin-rabbitmq"]={},_["@log4js-node/log4js-api"],_.moment,_.got,_.openmeteo))})(this,function(_,w,o,O,D){"use strict";var H=Object.defineProperty;var g=(_,w,o)=>w in _?H(_,w,{enumerable:!0,configurable:!0,writable:!0,value:o}):_[w]=o;var S=(_,w,o)=>(g(_,typeof w!="symbol"?w+"":w,o),o);let f;try{f=w.getLogger("meteo")}catch{}finally{}var F=(p=>(p.Arome="arome",p.IconEU="iconEu",p.GFS="gfs",p.GFSWave="gfsWave",p.NamConus="namConus",p.NamHawaii="namHawaii",p.NamAlaska="namAlaska",p.Geos5="geos5",p))(F||{});class U{static async queryPointMeteo(e,s,t,c=!1,a="",d={}){typeof t=="number"&&(t=t<1e12?t*1e3:t);const n=o(t),r={searchParams:{lng:e,lat:s,ts:n.valueOf(),params:c?"watertemp":void 0,source:a==null?void 0:a.toLowerCase()},timeout:3e4},h=o(),u=h.valueOf();n.isBefore(h.subtract(1,"month"))&&(f==null||f.warn("[%s] get history meteo on %s: %j",d.requestId,n.format(),r));const v="https://aod4idm.idmwx.com/api/ocean/point",i=await O.get(v,r).json(),l=o().valueOf();if(f==null||f.info("[%s] get meteo(cost: %d ms) from %s with options: %j",d.requestId,l-u,v,r),(i==null?void 0:i.code)===0)return{...i.data,source:a};f==null||f.warn("[%s] get meteo failed: %j",d.requestId,i)}static async queryPointFactor(e,s,t,c="wind,wave,current,watertemp,visibility",a="",d={}){typeof t=="number"&&(t=t<1e12?t*1e3:t);const n=o(t),r={searchParams:{lng:e,lat:s,ts:n.valueOf(),params:c,source:a==null?void 0:a.toLowerCase()},timeout:3e4},h=o(),u=h.valueOf();n.isBefore(h.subtract(1,"month"))&&(f==null||f.warn("[%s] get history factors on %s: %j",d.requestId,n.format(),r));const v="https://aod4idm.idmwx.com/api/ocean/factor",i=await O.get(v,r).json(),l=o().valueOf();if(f==null||f.info("[%s] get factors(cost: %d ms) from %s with options: %j",d.requestId,l-u,v,r),(i==null?void 0:i.code)===0)return{...i.data,source:a};f==null||f.warn("[%s] get factors failed: %j",d.requestId,i)}static async queryWindyPointForecast(e,s,t,c={}){const a="https://api.windy.com/api/point-forecast/v2",d=[];try{let n=o().valueOf();const r=await O.post(a,{headers:{"Content-Type":"application/json"},json:{lat:e,lon:s,key:t,model:"gfs",parameters:["temp","dewpoint","precip","convPrecip","snowPrecip","wind","windGust","cape","ptype","lclouds","mclouds","hclouds","rh","gh","pressure"],levels:["surface"]}}).json();let h=o().valueOf();f==null||f.info("[%s] get gfs-factors(cost: %d ms) from %s",c.requestId,h-n,a),n=h;const u=await O.post(a,{headers:{"Content-Type":"application/json"},json:{lat:e,lon:s,key:t,model:"gfsWave",parameters:["waves","windWaves","swell1","swell2"],levels:["surface"]}}).json();h=o().valueOf(),f==null||f.info("[%s] get gfs-wave-factors(cost: %d ms) from %s",c.requestId,h-n,a);for(let i=0;i<r.ts.length;i++){const l=this.populateUVFactor(r["wind_u-surface"][i],r["wind_v-surface"][i],!1,c);l.scale=this.calculateBeaufortWindForceScale(l.speed),d.push({utc:o(r.ts[i]).utc().format(),temp:r["temp-surface"][i]?Math.round((r["temp-surface"][i]-273.15)*100)/100:void 0,dp_temp:r["dewpoint-surface"][i]?Math.round((r["dewpoint-surface"][i]-273.15)*100)/100:void 0,precip:{inter3h:r["past3hprecip-surface"][i]?Math.round(r["past3hprecip-surface"][i]*1e3*1e3)/1e3:0,inter3hSnow:r["past3hsnowprecip-surface"][i]?Math.round(r["past3hsnowprecip-surface"][i]*1e3*1e3)/1e3:0,inter3hConv:r["past3hconvprecip-surface"][i]?Math.round(r["past3hconvprecip-surface"][i]*1e3*1e3)/1e3:0},wind:l,gusts:{speed:Math.round((r["gust-surface"][i]||0)*100)/100,kts:this.convertMs2Kts(r["gust-surface"][i])},lclouds:r["lclouds-surface"][i]?Math.round(r["lclouds-surface"][i]*100)/100:0,mclouds:r["mclouds-surface"][i]?Math.round(r["mclouds-surface"][i]*100)/100:0,hclouds:r["hclouds-surface"][i]?Math.round(r["hclouds-surface"][i]*100)/100:0,rh:r["rh-surface"][i]?Math.round(r["rh-surface"][i]*100)/100:0,gh:r["gh-surface"][i]?Math.round(r["gh-surface"][i]*100)/100:0,pressure:Math.round(r["pressure-surface"][i]/100*100)/100})}const v=[];for(let i=0;i<u.ts.length;i++){const l=this.calculateDouglasScale(u["waves_height-surface"][i],u["waves_direction-surface"][i],u["waves_period-surface"][i]),A=this.calculateDouglasScale(u["wwaves_height-surface"][i],u["wwaves_direction-surface"][i],u["wwaves_period-surface"][i]),R=this.calculateDouglasScale(u["swell1_height-surface"][i],u["swell1_direction-surface"][i],u["swell1_period-surface"][i]),y=this.calculateDouglasScale(u["swell2_height-surface"][i],u["swell2_direction-surface"][i],u["swell2_period-surface"][i]);v.push({utc:o(u.ts[i]).utc().format(),wave:{sig:l,wd:A,swell:R,swell2:y}})}for(const i of d){const l=v.find(A=>A.utc===i.utc);i.wave=l==null?void 0:l.wave}}catch(n){f.warn("[%s] get-gfs-factor failed: %s",c.requestId,n)}return d}static populateUVFactor(e,s,t=!1,c={}){const a=Math.round(Math.sqrt(Math.pow(e,2)+Math.pow(s,2))*1e4)/1e4,{degree:d,direction:n}=this.calculateUVDirection(e,s,t,c),r=this.convertMs2Kts(a);return{speed:a,kts:r,degree:d,direction:n,eastward:Math.round(e*1e4)/1e4,northward:Math.round(s*1e4)/1e4}}static convertMs2Kts(e){return isNaN(e)?0:Math.round(e*3600/1852*1e3)/1e3}static calculateUVDirection(e,s,t=!1,c={}){let a=Math.atan2(e,s)+Math.PI;t&&(a=Math.atan2(e,s));const d=this.convert2Direction(a);return a=Math.round(a/(2*Math.PI)*360*1e4)/1e4,{angle:a,degree:a,direction:d}}static convert2Direction(e){let s="N/A";if(!isNaN(e)){e<0&&(e+=2*Math.PI);const t=Math.PI/16;e<t?s="N":e>=t&&e<3*t?s="NNE":e>=3*t&&e<5*t?s="NE":e>=5*t&&e<7*t?s="ENE":e>=7*t&&e<9*t?s="E":e>=9*t&&e<11*t?s="ESE":e>=11*t&&e<13*t?s="SE":e>=13*t&&e<15*t?s="SSE":e>=15*t&&e<17*t?s="S":e>=17*t&&e<19*t?s="SSW":e>=19*t&&e<21*t?s="SW":e>=21*t&&e<23*t?s="WSW":e>=23*t&&e<25*t?s="W":e>=25*t&&e<27*t?s="WNW":e>=27*t&&e<29*t?s="NW":e>=29*t&&e<31*t?s="NNW":e>=31*t&&e<32*t&&(s="N")}return s}static calculateBeaufortWindForceScale(e){let s=0;return isNaN(e)||(e=Math.round(e*10)/10,e<=.2?s=0:e<=1.5?s=1:e<=3.3?s=2:e<=5.4?s=3:e<=7.9?s=4:e<=10.7?s=5:e<=13.8?s=6:e<=17.1?s=7:e<=20.7?s=8:e<=22.4?s=9:e<=28.4?s=10:e<=32.6?s=11:e>32.6&&(s=12)),s}static calculateDouglasScale(e,s,t){let c="Calm";const a=this.convert2Direction(s/360*2*Math.PI);return isNaN(e)||e<=.1?c="Calm":e<=.5?c="Smooth":e<=1.25?c="Slight":e<=2.5?c="Moderate":e<=4?c="Rough":e<=6?c="VeryRough":e<=9?c="High":e<=14?c="VeryHigh":c="Precipitous",{degree:Math.round(s*100)/100,scale:c,direction:a,height:Math.round(e*1e3)/1e3,period:Math.round(t*100)/100}}}let I;try{I=w.getLogger("open-meteo")}catch{}finally{}class C{constructor(e,s){S(this,"apikey");S(this,"debug");S(this,"FORECAST_URL","https://customer-api.open-meteo.com/v1/forecast");S(this,"SELF_FORECAST_URL","https://meteo2.idmwx.com/v1/forecast");S(this,"HISTORY_FORECAST_URL","https://customer-historical-forecast-api.open-meteo.com/v1/forecast");S(this,"MARINE_FORECAST_URL","https://customer-marine-api.open-meteo.com/v1/marine");S(this,"SELF_MARINE_FORECAST_URL","https://meteo2.idmwx.com/v1/marine");S(this,"WEATHER_VARIABLES",{NORMAL:{DAILY:"weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,precipitation_sum,precipitation_hours,precipitation_probability_max,wind_speed_10m_max,wind_gusts_10m_max,wind_direction_10m_dominant",HOURLY:"temperature_2m,relative_humidity_2m,dew_point_2m,apparent_temperature,precipitation_probability,precipitation,weather_code,visibility,wind_speed_10m,wind_direction_10m,wind_gusts_10m",CURRENT:"temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m"},SIMPLE:{DAILY:"weather_code",HOURLY:"wind_speed_10m,wind_direction_10m,wind_gusts_10m",CURRENT:"wind_speed_10m,wind_direction_10m,wind_gusts_10m"}});S(this,"MARINE_VARIABLES",{DAILY:"wave_height_max,wave_direction_dominant,wave_period_max,wind_wave_height_max,wind_wave_direction_dominant,wind_wave_period_max,wind_wave_peak_period_max,swell_wave_height_max,swell_wave_direction_dominant,swell_wave_period_max,swell_wave_peak_period_max",HOURLY:"wave_height,wave_direction,wave_period,wind_wave_height,wind_wave_direction,wind_wave_period,wind_wave_peak_period,swell_wave_height,swell_wave_direction,swell_wave_period,swell_wave_peak_period,ocean_current_velocity,ocean_current_direction",CURRENT:"wave_height,wave_direction,wave_period,wind_wave_height,wind_wave_direction,wind_wave_period,wind_wave_peak_period,swell_wave_height,swell_wave_direction,swell_wave_period,swell_wave_peak_period,ocean_current_velocity,ocean_current_direction"});this.apikey=e||"smE3JnDLHy3TizVv",this.debug=s}range(e,s,t){return Array.from({length:(s-e)/t},(c,a)=>e+a*t)}async weatherForecast(e,s={}){const t=o(),c=await D.fetchWeatherApi(e.url,e);delete e.apikey;const a=o();return this.debug&&I.info("[%s] fetch weather api (%s) cost: %d ms",s.requestId,e.url,a.diff(t,"ms")),this.parseWeatherData(c,e,s)}async marineForecast(e,s={}){const t=o(),c=await D.fetchWeatherApi(e.url,e);delete e.apikey;const a=o();return this.debug&&I.info("[%s] fetch marine api (%s) cost: %d ms",s.requestId,e.url,a.diff(t,"ms")),this.parseWeatherData(c,e,s)}async parseWeatherData(e,s,t={}){var d,n,r,h,u,v;const c=[],a=Math.pow(10,t.precision||6);for(const i of e){const l=i.utcOffsetSeconds(),A=i.current(),R=i.hourly(),y=i.daily(),M={};if(A){const L=o();M.current={time:o.unix(Number(A.time())+l).utc().format()};for(let m=0;m<A.variablesLength();m++){const E=A.variables(m).value();M.current[s.current[m]]=Number.isNaN(E)?E:Math.round(E*a)/a}const N=o();this.debug&&I.info("[%s] fetch current variables cost: %d ms",t.requestId,N.diff(L,"ms"))}if(R){const L=o(),N=o.unix(Number(R.time())+l);M.hourly={date:N.utc().format(),time:this.range(Number(R.time()),Number(R.timeEnd()),R.interval()).map(E=>o.unix(E+l).diff(N,"h"))};for(let E=0;E<R.variablesLength();E++){const b=(r=(n=(d=R.variables(E).valuesArray())==null?void 0:d.toString())==null?void 0:n.split(","))==null?void 0:r.map(T=>Number.isNaN(T)?T:Math.round(Number(T)*a)/a);M.hourly[s.hourly[E]]=b}const m=o();this.debug&&I.info("[%s] fetch hourly variables cost: %d ms",t.requestId,m.diff(L,"ms"))}if(y){const L=o();M.daily={time:this.range(Number(y.time()),Number(y.timeEnd()),y.interval()).map(m=>o.unix(m+l).utc().format())};for(let m=0;m<y.variablesLength();m++){const E=(v=(u=(h=y.variables(m).valuesArray())==null?void 0:h.toString())==null?void 0:u.split(","))==null?void 0:v.map(b=>Number.isNaN(b)?b:Math.round(Number(b)*a)/a);M.daily[s.daily[m]]=E}const N=o();this.debug&&I.info("[%s] fetch daily variables cost: %d ms",t.requestId,N.diff(L,"ms"))}c.push(M)}return c}prepare(e,s,t,c={}){e?(t.start_date=e.utc().set({minute:0,second:0,millisecond:0}).format("YYYY-MM-DD"),t.end_date=e.clone().add(c.forecastDays??1,"d").utc().format("YYYY-MM-DD")):t.forecast_days=c.forecastDays;const a=o().subtract(1,"d");return s?(t.url=c.selfHosted?this.SELF_MARINE_FORECAST_URL:this.MARINE_FORECAST_URL,e!=null&&e.isBefore(a)&&(t.url=this.MARINE_FORECAST_URL)):(t.url=c.selfHosted?this.SELF_FORECAST_URL:this.FORECAST_URL,e!=null&&e.isBefore(a)&&(t.url=this.HISTORY_FORECAST_URL)),t}async spotForecast(e,s,t,c=!0,a=!0,d=!1,n={forecastDays:1,precision:6,selfHosted:!0}){var i,l,A,R,y,M,L,N;const r={apikey:this.apikey,latitude:e,longitude:s,cell_selection:"sea",wind_speed_unit:"kn",models:((i=n.weatherModels)==null?void 0:i.split(","))||["best_match"],timezone:"auto"};a&&(r.daily=c?((l=this.WEATHER_VARIABLES.SIMPLE.DAILY)==null?void 0:l.split(","))||[]:((A=this.WEATHER_VARIABLES.NORMAL.DAILY)==null?void 0:A.split(","))||[]),t||(n.forecastDays=n.forecastDays||1,r.current=c?((R=this.WEATHER_VARIABLES.SIMPLE.CURRENT)==null?void 0:R.split(","))||[]:((y=this.WEATHER_VARIABLES.NORMAL.CURRENT)==null?void 0:y.split(","))||[]),d&&(r.hourly=c?((M=this.WEATHER_VARIABLES.SIMPLE.HOURLY)==null?void 0:M.split(","))||[]:((L=this.WEATHER_VARIABLES.NORMAL.HOURLY)==null?void 0:L.split(","))||[]),this.prepare(t,!1,r,n);const h=await this.weatherForecast(r,n),u={apikey:this.apikey,latitude:e,longitude:s,cell_selection:"sea",timezone:"auto",wind_speed_unit:"kn",models:((N=n.marineModels)==null?void 0:N.split(","))||["best_match"]};a&&(u.daily=this.MARINE_VARIABLES.DAILY.split(",")),t||(n.forecastDays=n.forecastDays||1,u.current=this.MARINE_VARIABLES.CURRENT.split(",")),d&&(u.hourly=this.MARINE_VARIABLES.HOURLY.split(",")),this.prepare(t,!0,u,n);const v=await this.marineForecast(u,n);return{weather:h,marine:v}}}_.MeteoHelper=U,_.MeteoHelper2=C,_.WindyModel=F,Object.defineProperty(_,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
/**
|
|
3
|
+
* 大气海洋要素
|
|
4
|
+
*/
|
|
5
|
+
export interface OMParams {
|
|
6
|
+
latitude: number | number[];
|
|
7
|
+
longitude: number | number[];
|
|
8
|
+
elevation?: number;
|
|
9
|
+
hourly?: string | string[];
|
|
10
|
+
current?: string | string[];
|
|
11
|
+
daily?: string | string[];
|
|
12
|
+
timezone?: string;
|
|
13
|
+
temperature_unit?: string;
|
|
14
|
+
wind_speed_unit?: string;
|
|
15
|
+
precipitation_unit?: string;
|
|
16
|
+
models?: string | string[];
|
|
17
|
+
forecast_days?: number;
|
|
18
|
+
past_days?: number;
|
|
19
|
+
forecast_hours?: number;
|
|
20
|
+
start_date?: string;
|
|
21
|
+
start_hour?: string;
|
|
22
|
+
end_date?: string;
|
|
23
|
+
end_hour?: string;
|
|
24
|
+
cell_selection?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
apikey?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 请求参数
|
|
30
|
+
*/
|
|
31
|
+
export interface OMOptions {
|
|
32
|
+
requestId?: string;
|
|
33
|
+
pastDays?: number;
|
|
34
|
+
forecastDays?: number;
|
|
35
|
+
weatherModels?: string;
|
|
36
|
+
marineModels?: string;
|
|
37
|
+
precision?: number;
|
|
38
|
+
selfHosted?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @see https://open-meteo.com/en/docs
|
|
42
|
+
*/
|
|
43
|
+
export declare class MeteoHelper2 {
|
|
44
|
+
private readonly apikey;
|
|
45
|
+
private readonly debug;
|
|
46
|
+
private readonly FORECAST_URL;
|
|
47
|
+
private readonly SELF_FORECAST_URL;
|
|
48
|
+
private readonly HISTORY_FORECAST_URL;
|
|
49
|
+
private readonly MARINE_FORECAST_URL;
|
|
50
|
+
private readonly SELF_MARINE_FORECAST_URL;
|
|
51
|
+
private readonly WEATHER_VARIABLES;
|
|
52
|
+
private readonly MARINE_VARIABLES;
|
|
53
|
+
constructor(apikey: string, debug?: boolean);
|
|
54
|
+
range(start: number, stop: number, step: number): number[];
|
|
55
|
+
weatherForecast(params: any, options?: OMOptions): Promise<any[]>;
|
|
56
|
+
marineForecast(params: any, options?: OMOptions): Promise<any[]>;
|
|
57
|
+
private parseWeatherData;
|
|
58
|
+
/**
|
|
59
|
+
* 完善start_hour/end_hour 或者是forecast_days变量,二选一
|
|
60
|
+
* @param datetime 从指定时间开始,不指定时从当前时间开始
|
|
61
|
+
* @param params 要查询的要素
|
|
62
|
+
* @param options 请求参数
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
private prepare;
|
|
66
|
+
spotForecast(lat: number, lng: number, datetime: moment.Moment | undefined, simplify?: boolean, withDaily?: boolean, withHourly?: boolean, options?: OMOptions): Promise<{
|
|
67
|
+
weather: any[];
|
|
68
|
+
marine: any[];
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 风要素
|
|
3
|
+
*/
|
|
4
|
+
export interface Wind {
|
|
5
|
+
speed: number;
|
|
6
|
+
kts: number;
|
|
7
|
+
scale: number;
|
|
8
|
+
degree: number;
|
|
9
|
+
direction: string;
|
|
10
|
+
eastward: number;
|
|
11
|
+
northward: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 浪要素
|
|
15
|
+
*/
|
|
16
|
+
export interface Wave {
|
|
17
|
+
degree: number;
|
|
18
|
+
height: number;
|
|
19
|
+
period: number;
|
|
20
|
+
direction: string;
|
|
21
|
+
scale: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 洋流要素
|
|
25
|
+
*/
|
|
26
|
+
export interface Current {
|
|
27
|
+
degree: number;
|
|
28
|
+
eastward: number;
|
|
29
|
+
northward: number;
|
|
30
|
+
speed: number;
|
|
31
|
+
direction: string;
|
|
32
|
+
kts: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 组合浪
|
|
36
|
+
*/
|
|
37
|
+
export interface WaveComponent {
|
|
38
|
+
sig: Wave;
|
|
39
|
+
swell: Wave;
|
|
40
|
+
wd: Wave;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 风浪流气象要素
|
|
44
|
+
*/
|
|
45
|
+
export interface Meteo {
|
|
46
|
+
utc: string;
|
|
47
|
+
wind: Wind;
|
|
48
|
+
wave: WaveComponent;
|
|
49
|
+
current: Current;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Arome - covers France and surrounding areas
|
|
53
|
+
* IconEU - covers Europe and surrounding areas
|
|
54
|
+
* GFS - a global model
|
|
55
|
+
* GFS Wave - a global model EXCLUDING Hudson bay (partly), Black Sea, Caspian Sea, most of the Arctic Ocean.
|
|
56
|
+
* namConus - covers the USA and surrounding areas (Canada, Mexico)
|
|
57
|
+
* namHawaii - covers Hawaii
|
|
58
|
+
* namAlaska - covers Alaska and surrounding areas.
|
|
59
|
+
* geos5 - a global model
|
|
60
|
+
*/
|
|
61
|
+
export declare enum WindyModel {
|
|
62
|
+
Arome = "arome",
|
|
63
|
+
IconEU = "iconEu",
|
|
64
|
+
GFS = "gfs",
|
|
65
|
+
GFSWave = "gfsWave",
|
|
66
|
+
NamConus = "namConus",
|
|
67
|
+
NamHawaii = "namHawaii",
|
|
68
|
+
NamAlaska = "namAlaska",
|
|
69
|
+
Geos5 = "geos5"
|
|
70
|
+
}
|
|
71
|
+
export declare class MeteoHelper {
|
|
72
|
+
/**
|
|
73
|
+
* 点查海洋气象要素(全量)
|
|
74
|
+
* @param lng
|
|
75
|
+
* @param lat
|
|
76
|
+
* @param timestamp 单位毫秒
|
|
77
|
+
* @param wt 是否查海温
|
|
78
|
+
* @param source
|
|
79
|
+
* @param options
|
|
80
|
+
*/
|
|
81
|
+
static queryPointMeteo(lng: number, lat: number, timestamp: number | string, wt?: boolean, source?: string, options?: {
|
|
82
|
+
requestId?: string;
|
|
83
|
+
}): Promise<Meteo | undefined>;
|
|
84
|
+
/**
|
|
85
|
+
* 点查海洋气象要素(指定要素组合)
|
|
86
|
+
* @param lng
|
|
87
|
+
* @param lat
|
|
88
|
+
* @param timestamp 单位毫秒
|
|
89
|
+
* @param params 要查询的要素组合
|
|
90
|
+
* 1) wind, temp, prmsl, precip 来自于同一预报文件, 查wind同时也带出其它要素, 支持gfs和cmems
|
|
91
|
+
* 2) wave 支持gfs和cmems
|
|
92
|
+
* 3) current 来自smoc, 不区分gfs和cmems
|
|
93
|
+
* 4) watertemp 来自cmems, 不区分gfs和cmems
|
|
94
|
+
* 5)visibility gusts cloud weather dp_temp 仅支持gfs
|
|
95
|
+
* @param source
|
|
96
|
+
* @param options
|
|
97
|
+
*/
|
|
98
|
+
static queryPointFactor(lng: number, lat: number, timestamp: number | string, params?: string, source?: string, options?: {
|
|
99
|
+
requestId?: string;
|
|
100
|
+
}): Promise<Meteo | undefined>;
|
|
101
|
+
/**
|
|
102
|
+
* @see https://api.windy.com/point-forecast/docs
|
|
103
|
+
* @param key
|
|
104
|
+
*/
|
|
105
|
+
static queryWindyPointForecast(lat: number, lng: number, key: string, options?: {
|
|
106
|
+
requestId?: string;
|
|
107
|
+
}): Promise<any[]>;
|
|
108
|
+
/**
|
|
109
|
+
* 填充UV向量
|
|
110
|
+
* @param eastward m/s
|
|
111
|
+
* @param northward m/s
|
|
112
|
+
* @param reverse 反转, 如计算洋流方向时,风来流去
|
|
113
|
+
* @param options
|
|
114
|
+
*/
|
|
115
|
+
static populateUVFactor(eastward: number, northward: number, reverse?: boolean, options?: {
|
|
116
|
+
requestId?: string;
|
|
117
|
+
}): {
|
|
118
|
+
speed: number;
|
|
119
|
+
kts: number;
|
|
120
|
+
degree: number;
|
|
121
|
+
direction: string;
|
|
122
|
+
eastward: number;
|
|
123
|
+
northward: number;
|
|
124
|
+
};
|
|
125
|
+
static convertMs2Kts(speed: number): number;
|
|
126
|
+
/**
|
|
127
|
+
* 计算UV向量方向
|
|
128
|
+
* @param eastward m/s
|
|
129
|
+
* @param northward m/s
|
|
130
|
+
* @param reverse 反转, 如计算洋流方向时,风来流去
|
|
131
|
+
* @param options
|
|
132
|
+
*/
|
|
133
|
+
static calculateUVDirection(eastward: number, northward: number, reverse?: boolean, options?: {
|
|
134
|
+
requestId?: string;
|
|
135
|
+
}): {
|
|
136
|
+
angle: number;
|
|
137
|
+
degree: number;
|
|
138
|
+
direction: string;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* 将弧度转换为方向
|
|
142
|
+
* @param delta 弧度
|
|
143
|
+
* @return {string}
|
|
144
|
+
*/
|
|
145
|
+
static convert2Direction(angle: number): string;
|
|
146
|
+
/**
|
|
147
|
+
* 计算风力蒲福等级
|
|
148
|
+
* @param speed m/s
|
|
149
|
+
* @returns {number}
|
|
150
|
+
*/
|
|
151
|
+
static calculateBeaufortWindForceScale(speed: number): number;
|
|
152
|
+
/**
|
|
153
|
+
* 计算海浪等级
|
|
154
|
+
* @param height 高度 m
|
|
155
|
+
* @param degree 度
|
|
156
|
+
* @param period 周期 s
|
|
157
|
+
*/
|
|
158
|
+
static calculateDouglasScale(height: number, degree: number, period: number): {
|
|
159
|
+
degree: number;
|
|
160
|
+
scale: string;
|
|
161
|
+
direction: string;
|
|
162
|
+
height: number;
|
|
163
|
+
period: number;
|
|
164
|
+
};
|
|
165
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@idm-plugin/meteo2",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "idm plugin for meteo2(open meteo)",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"idm",
|
|
9
|
+
"meteo2",
|
|
10
|
+
"open meteo"
|
|
11
|
+
],
|
|
12
|
+
"author": "chenheng@idmwx.com",
|
|
13
|
+
"contributors": [
|
|
14
|
+
"ChenHeng"
|
|
15
|
+
],
|
|
16
|
+
"license": "GPL-3.0",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"main": "./dist/index.umd.cjs",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "index.d.ts",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "vite --config ./build/base.config.ts",
|
|
25
|
+
"build": "vite build --config ./build/lib.config.ts",
|
|
26
|
+
"lint:fix": "eslint --fix --ext .js,.ts,.",
|
|
27
|
+
"prettier": "yarn lint:fix && prettier --write '**/*.{js,ts}'",
|
|
28
|
+
"release": "yarn build && yarn publish --access public"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@log4js-node/log4js-api": "^1.0.2",
|
|
32
|
+
"got": "11",
|
|
33
|
+
"moment": "^2.30.1",
|
|
34
|
+
"openmeteo": "^1.1.4"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/jest": "^25.2.2",
|
|
38
|
+
"@types/node": "^18.14.2",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
|
40
|
+
"@typescript-eslint/parser": "^5.53.0",
|
|
41
|
+
"@vitejs/plugin-vue": "^4.2.3",
|
|
42
|
+
"cross-fetch": "^4.1.0",
|
|
43
|
+
"eslint": "^8.35.0",
|
|
44
|
+
"eslint-config-prettier": "^8.6.0",
|
|
45
|
+
"eslint-define-config": "^1.15.0",
|
|
46
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
47
|
+
"jest": "^26.6.3",
|
|
48
|
+
"lint-staged": "^13.1.2",
|
|
49
|
+
"log4js": "^6.9.1",
|
|
50
|
+
"prettier": "^2.8.4",
|
|
51
|
+
"sass": "^1.58.3",
|
|
52
|
+
"simple-git-hooks": "^2.8.1",
|
|
53
|
+
"stylelint": "^15.2.0",
|
|
54
|
+
"supertest": "^4.0.2",
|
|
55
|
+
"ts-jest": "^26.5.3",
|
|
56
|
+
"ts-node-dev": "^2.0.0-0",
|
|
57
|
+
"tsconfig-paths": "^3.12.0",
|
|
58
|
+
"typescript": "^4.9.3",
|
|
59
|
+
"vite": "^4.1.0",
|
|
60
|
+
"vite-plugin-dts": "^2.0.2",
|
|
61
|
+
"vite-plugin-static-copy": "^0.17.0"
|
|
62
|
+
},
|
|
63
|
+
"lint-staged": {
|
|
64
|
+
"*.{ts,tsx,js}": "eslint --fix",
|
|
65
|
+
"*.{ts,tsx,js,scss}": "prettier --write"
|
|
66
|
+
},
|
|
67
|
+
"simple-git-hooks": {
|
|
68
|
+
"pre-commit": "yarn exec lint-staged"
|
|
69
|
+
}
|
|
70
|
+
}
|