mostlyright 1.1.2 → 1.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/README.md +3 -3
- package/dist/{chunk-465FJZTS.mjs → chunk-64FNXDHG.mjs} +2 -2
- package/dist/{chunk-IN2UOZYO.mjs → chunk-WKPNA46C.mjs} +40 -2
- package/dist/chunk-WKPNA46C.mjs.map +1 -0
- package/dist/{iem-asos-ZPUMH3KM.mjs → iem-asos-2D5EP2FT.mjs} +3 -3
- package/dist/index.bundle.mjs +1794 -167
- package/dist/index.bundle.mjs.map +1 -1
- package/dist/index.cjs +90 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -3
- package/dist/index.d.ts +60 -3
- package/dist/index.global.js +1843 -169
- package/dist/index.global.js.map +1 -1
- package/dist/index.mjs +91 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{src-NP2MZ322.mjs → src-BBELB6SP.mjs} +6 -2
- package/package.json +36 -5
- package/dist/chunk-IN2UOZYO.mjs.map +0 -1
- /package/dist/{chunk-465FJZTS.mjs.map → chunk-64FNXDHG.mjs.map} +0 -0
- /package/dist/{iem-asos-ZPUMH3KM.mjs.map → iem-asos-2D5EP2FT.mjs.map} +0 -0
- /package/dist/{src-NP2MZ322.mjs.map → src-BBELB6SP.mjs.map} +0 -0
package/dist/index.bundle.mjs
CHANGED
|
@@ -4,12 +4,14 @@ import {
|
|
|
4
4
|
buildIemUrl,
|
|
5
5
|
downloadIemAsos,
|
|
6
6
|
yearlyChunksExclusiveEnd
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-64FNXDHG.mjs";
|
|
8
8
|
import {
|
|
9
9
|
CLIMATE_REPORT_TYPE_PRIORITY,
|
|
10
|
+
DataAvailabilityError,
|
|
10
11
|
LiveStreamError,
|
|
11
12
|
NoLiveDataError,
|
|
12
13
|
NotFoundError,
|
|
14
|
+
STATIONS,
|
|
13
15
|
STATION_BY_CODE,
|
|
14
16
|
STATION_BY_ICAO,
|
|
15
17
|
SourceMismatchError,
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
helloCore,
|
|
19
21
|
settlementDateFor,
|
|
20
22
|
src_exports
|
|
21
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WKPNA46C.mjs";
|
|
22
24
|
import {
|
|
23
25
|
awcToObservation,
|
|
24
26
|
celsiusToFahrenheit,
|
|
@@ -72,6 +74,7 @@ __export(src_exports2, {
|
|
|
72
74
|
SUPPORTED_SOURCES: () => SUPPORTED_SOURCES,
|
|
73
75
|
awcToObservation: () => awcToObservation,
|
|
74
76
|
buildIemUrl: () => buildIemUrl,
|
|
77
|
+
dailyExtremes: () => dailyExtremes,
|
|
75
78
|
downloadCli: () => downloadCli,
|
|
76
79
|
downloadCliRange: () => downloadCliRange,
|
|
77
80
|
downloadGhcnh: () => downloadGhcnh,
|
|
@@ -90,12 +93,14 @@ __export(src_exports2, {
|
|
|
90
93
|
latest: () => latest,
|
|
91
94
|
mapCloudCover: () => mapCloudCover,
|
|
92
95
|
mergeClimate: () => mergeClimate,
|
|
96
|
+
obs: () => obs,
|
|
93
97
|
parseAwcVisibility: () => parseAwcVisibility,
|
|
94
98
|
parseCliRecord: () => parseCliRecord,
|
|
95
99
|
parseCliResponse: () => parseCliResponse,
|
|
96
100
|
parseGhcnhPsv: () => parseGhcnhPsv,
|
|
97
101
|
parseGhcnhRow: () => parseGhcnhRow,
|
|
98
102
|
parseIemCsv: () => parseIemCsv,
|
|
103
|
+
resolveAutoStrategy: () => resolveAutoStrategy,
|
|
99
104
|
sourceTag: () => sourceTag,
|
|
100
105
|
stream: () => stream,
|
|
101
106
|
validatePollSeconds: () => validatePollSeconds,
|
|
@@ -289,10 +294,10 @@ function inferReportType(product, observationDate) {
|
|
|
289
294
|
if (!product) return "preliminary";
|
|
290
295
|
const issued = parseProductTimestamp(product);
|
|
291
296
|
if (issued === null) return "preliminary";
|
|
292
|
-
const
|
|
293
|
-
if (
|
|
297
|
+
const obs2 = parseObservationDate(observationDate);
|
|
298
|
+
if (obs2 === null) return "preliminary";
|
|
294
299
|
const issuedDayUtc = Date.UTC(issued.getUTCFullYear(), issued.getUTCMonth(), issued.getUTCDate());
|
|
295
|
-
const obsDayUtc =
|
|
300
|
+
const obsDayUtc = obs2.getTime();
|
|
296
301
|
const deltaDays = Math.round((issuedDayUtc - obsDayUtc) / 864e5);
|
|
297
302
|
if (deltaDays <= 0) return "preliminary";
|
|
298
303
|
if (deltaDays === 1) {
|
|
@@ -655,8 +660,8 @@ function parseGhcnhPsv(psvBody) {
|
|
|
655
660
|
const key = header[c];
|
|
656
661
|
row[key] = c < cells.length ? cells[c] : "";
|
|
657
662
|
}
|
|
658
|
-
const
|
|
659
|
-
if (
|
|
663
|
+
const obs2 = parseGhcnhRow(row);
|
|
664
|
+
if (obs2 !== null) out.push(obs2);
|
|
660
665
|
}
|
|
661
666
|
return out;
|
|
662
667
|
}
|
|
@@ -719,8 +724,8 @@ function normalizeStation(station) {
|
|
|
719
724
|
if (s.length === 3) return `K${s}`;
|
|
720
725
|
return s;
|
|
721
726
|
}
|
|
722
|
-
function asLiveObservation(
|
|
723
|
-
return { ...
|
|
727
|
+
function asLiveObservation(obs2, tag) {
|
|
728
|
+
return { ...obs2, source: tag };
|
|
724
729
|
}
|
|
725
730
|
async function fetchAwcLatest(station) {
|
|
726
731
|
const icao = normalizeStation(station);
|
|
@@ -728,9 +733,9 @@ async function fetchAwcLatest(station) {
|
|
|
728
733
|
const tag = sourceTag("awc");
|
|
729
734
|
const rows = [];
|
|
730
735
|
for (const m of raw) {
|
|
731
|
-
const
|
|
732
|
-
if (
|
|
733
|
-
rows.push(asLiveObservation(
|
|
736
|
+
const obs2 = awcToObservation(m);
|
|
737
|
+
if (obs2 !== null) {
|
|
738
|
+
rows.push(asLiveObservation(obs2, tag));
|
|
734
739
|
}
|
|
735
740
|
}
|
|
736
741
|
return rows;
|
|
@@ -762,9 +767,9 @@ function previousDayIso(iso) {
|
|
|
762
767
|
}
|
|
763
768
|
async function fetchIemLatest(station) {
|
|
764
769
|
const [{ fetchWithRetry: fetchWithRetry2 }, { STATION_CODE_RE: STATION_CODE_RE3 }, { buildIemUrl: buildIemUrl2 }, { parseIemCsv: parseIemCsv2 }] = await Promise.all([
|
|
765
|
-
import("./src-
|
|
770
|
+
import("./src-BBELB6SP.mjs"),
|
|
766
771
|
import("./bounds-KSTXL77E.mjs"),
|
|
767
|
-
import("./iem-asos-
|
|
772
|
+
import("./iem-asos-2D5EP2FT.mjs"),
|
|
768
773
|
import("./iem-IO2HIL5V.mjs")
|
|
769
774
|
]);
|
|
770
775
|
const icao = normalizeStation(station);
|
|
@@ -786,8 +791,8 @@ async function fetchIemLatest(station) {
|
|
|
786
791
|
const response = await fetchWithRetry2(url);
|
|
787
792
|
const csv = await response.text();
|
|
788
793
|
const override = reportType === 3 ? "METAR" : "SPECI";
|
|
789
|
-
const
|
|
790
|
-
for (const row of
|
|
794
|
+
const obs2 = parseIemCsv2(csv, { observationTypeOverride: override });
|
|
795
|
+
for (const row of obs2) {
|
|
791
796
|
rows.push(asLiveObservation(row, tag));
|
|
792
797
|
}
|
|
793
798
|
}
|
|
@@ -1000,10 +1005,1379 @@ async function iemMosForecasts(station, fromDate, toDate, opts = {}) {
|
|
|
1000
1005
|
}
|
|
1001
1006
|
|
|
1002
1007
|
// ../weather/src/forecasts/nwp-stub.ts
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1008
|
+
var IEM_MOS_COVERED_STATIONS = /* @__PURE__ */ new Set([
|
|
1009
|
+
"KNYC",
|
|
1010
|
+
"KLAX",
|
|
1011
|
+
"KORD",
|
|
1012
|
+
"KMIA",
|
|
1013
|
+
"KDEN",
|
|
1014
|
+
"KSEA",
|
|
1015
|
+
"KATL"
|
|
1016
|
+
]);
|
|
1017
|
+
function buildHint(station, model) {
|
|
1018
|
+
const hasMosCoverage = IEM_MOS_COVERED_STATIONS.has(station.toUpperCase());
|
|
1019
|
+
const mosLine = hasMosCoverage ? `Workaround for ${station}: iemMosForecasts("${station}", ...) is available (IEM MOS catalog covers this station).` : `Workaround: this station has no IEM MOS coverage; use the Python SDK's mostlyright.forecast_nwp() in v1.x.`;
|
|
1020
|
+
return `forecastNwp(${station}, "${model}") is a v1.x stub. Browser GRIB2 decode is not production-ready in May 2026 (no eccodes / cfgrib equivalent for the browser; WASM compile time + bundle size make it impractical for v1.x). ${mosLine} See https://mostlyright.md/docs/sdk/typescript/forecasts#typescript-lane for the architectural reason + v1.1+ tracking.`;
|
|
1021
|
+
}
|
|
1022
|
+
async function forecastNwp(station, model, _opts = {}) {
|
|
1023
|
+
throw new DataAvailabilityError({
|
|
1024
|
+
reason: "model_unavailable",
|
|
1025
|
+
source: "nwp-stub",
|
|
1026
|
+
hint: buildHint(station, model)
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// ../core/dist/discovery/index.mjs
|
|
1031
|
+
var STATIONS2 = [
|
|
1032
|
+
{
|
|
1033
|
+
code: "EDDB",
|
|
1034
|
+
country: "DE",
|
|
1035
|
+
ghcnh_id: null,
|
|
1036
|
+
icao: "EDDB",
|
|
1037
|
+
latitude: 52.3667,
|
|
1038
|
+
longitude: 13.5033,
|
|
1039
|
+
name: "Berlin Brandenburg",
|
|
1040
|
+
tz: "Europe/Berlin"
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
code: "EDDF",
|
|
1044
|
+
country: "DE",
|
|
1045
|
+
ghcnh_id: null,
|
|
1046
|
+
icao: "EDDF",
|
|
1047
|
+
latitude: 50.0379,
|
|
1048
|
+
longitude: 8.5622,
|
|
1049
|
+
name: "Frankfurt am Main",
|
|
1050
|
+
tz: "Europe/Berlin"
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
code: "EDDM",
|
|
1054
|
+
country: "DE",
|
|
1055
|
+
ghcnh_id: null,
|
|
1056
|
+
icao: "EDDM",
|
|
1057
|
+
latitude: 48.3538,
|
|
1058
|
+
longitude: 11.7861,
|
|
1059
|
+
name: "Munich Franz Josef Strauss",
|
|
1060
|
+
tz: "Europe/Berlin"
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
code: "EFHK",
|
|
1064
|
+
country: "FI",
|
|
1065
|
+
ghcnh_id: null,
|
|
1066
|
+
icao: "EFHK",
|
|
1067
|
+
latitude: 60.3172,
|
|
1068
|
+
longitude: 24.9633,
|
|
1069
|
+
name: "Helsinki-Vantaa",
|
|
1070
|
+
tz: "Europe/Helsinki"
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
code: "EGKK",
|
|
1074
|
+
country: "GB",
|
|
1075
|
+
ghcnh_id: null,
|
|
1076
|
+
icao: "EGKK",
|
|
1077
|
+
latitude: 51.1481,
|
|
1078
|
+
longitude: -0.1903,
|
|
1079
|
+
name: "London Gatwick",
|
|
1080
|
+
tz: "Europe/London"
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
code: "EGLL",
|
|
1084
|
+
country: "GB",
|
|
1085
|
+
ghcnh_id: null,
|
|
1086
|
+
icao: "EGLL",
|
|
1087
|
+
latitude: 51.4706,
|
|
1088
|
+
longitude: -0.4619,
|
|
1089
|
+
name: "London Heathrow",
|
|
1090
|
+
tz: "Europe/London"
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
code: "EHAM",
|
|
1094
|
+
country: "NL",
|
|
1095
|
+
ghcnh_id: null,
|
|
1096
|
+
icao: "EHAM",
|
|
1097
|
+
latitude: 52.3086,
|
|
1098
|
+
longitude: 4.7639,
|
|
1099
|
+
name: "Amsterdam Schiphol",
|
|
1100
|
+
tz: "Europe/Amsterdam"
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
code: "EKCH",
|
|
1104
|
+
country: "DK",
|
|
1105
|
+
ghcnh_id: null,
|
|
1106
|
+
icao: "EKCH",
|
|
1107
|
+
latitude: 55.6181,
|
|
1108
|
+
longitude: 12.6561,
|
|
1109
|
+
name: "Copenhagen Kastrup",
|
|
1110
|
+
tz: "Europe/Copenhagen"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
code: "EPWA",
|
|
1114
|
+
country: "PL",
|
|
1115
|
+
ghcnh_id: null,
|
|
1116
|
+
icao: "EPWA",
|
|
1117
|
+
latitude: 52.1657,
|
|
1118
|
+
longitude: 20.9671,
|
|
1119
|
+
name: "Warsaw Chopin",
|
|
1120
|
+
tz: "Europe/Warsaw"
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
code: "ESSA",
|
|
1124
|
+
country: "SE",
|
|
1125
|
+
ghcnh_id: null,
|
|
1126
|
+
icao: "ESSA",
|
|
1127
|
+
latitude: 59.6519,
|
|
1128
|
+
longitude: 17.9186,
|
|
1129
|
+
name: "Stockholm Arlanda",
|
|
1130
|
+
tz: "Europe/Stockholm"
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
code: "ATL",
|
|
1134
|
+
country: "US",
|
|
1135
|
+
ghcnh_id: "USW00013874",
|
|
1136
|
+
icao: "KATL",
|
|
1137
|
+
latitude: 33.6407,
|
|
1138
|
+
longitude: -84.4277,
|
|
1139
|
+
name: "Hartsfield-Jackson Atlanta International",
|
|
1140
|
+
tz: "America/New_York"
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
code: "AUS",
|
|
1144
|
+
country: "US",
|
|
1145
|
+
ghcnh_id: "USW00013904",
|
|
1146
|
+
icao: "KAUS",
|
|
1147
|
+
latitude: 30.1975,
|
|
1148
|
+
longitude: -97.6664,
|
|
1149
|
+
name: "Austin-Bergstrom International",
|
|
1150
|
+
tz: "America/Chicago"
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
code: "BOS",
|
|
1154
|
+
country: "US",
|
|
1155
|
+
ghcnh_id: "USW00014739",
|
|
1156
|
+
icao: "KBOS",
|
|
1157
|
+
latitude: 42.3656,
|
|
1158
|
+
longitude: -71.0096,
|
|
1159
|
+
name: "Boston Logan International",
|
|
1160
|
+
tz: "America/New_York"
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
code: "DCA",
|
|
1164
|
+
country: "US",
|
|
1165
|
+
ghcnh_id: "USW00013743",
|
|
1166
|
+
icao: "KDCA",
|
|
1167
|
+
latitude: 38.8512,
|
|
1168
|
+
longitude: -77.0402,
|
|
1169
|
+
name: "Washington Reagan National",
|
|
1170
|
+
tz: "America/New_York"
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
code: "DEN",
|
|
1174
|
+
country: "US",
|
|
1175
|
+
ghcnh_id: "USW00003017",
|
|
1176
|
+
icao: "KDEN",
|
|
1177
|
+
latitude: 39.8561,
|
|
1178
|
+
longitude: -104.6737,
|
|
1179
|
+
name: "Denver International",
|
|
1180
|
+
tz: "America/Denver"
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
code: "DFW",
|
|
1184
|
+
country: "US",
|
|
1185
|
+
ghcnh_id: "USW00003927",
|
|
1186
|
+
icao: "KDFW",
|
|
1187
|
+
latitude: 32.8998,
|
|
1188
|
+
longitude: -97.0403,
|
|
1189
|
+
name: "Dallas-Fort Worth International",
|
|
1190
|
+
tz: "America/Chicago"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
code: "HOU",
|
|
1194
|
+
country: "US",
|
|
1195
|
+
ghcnh_id: "USW00012918",
|
|
1196
|
+
icao: "KHOU",
|
|
1197
|
+
latitude: 29.6454,
|
|
1198
|
+
longitude: -95.2789,
|
|
1199
|
+
name: "Houston Hobby",
|
|
1200
|
+
tz: "America/Chicago"
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
code: "LAS",
|
|
1204
|
+
country: "US",
|
|
1205
|
+
ghcnh_id: "USW00023169",
|
|
1206
|
+
icao: "KLAS",
|
|
1207
|
+
latitude: 36.084,
|
|
1208
|
+
longitude: -115.1537,
|
|
1209
|
+
name: "Harry Reid (McCarran) International",
|
|
1210
|
+
tz: "America/Los_Angeles"
|
|
1211
|
+
},
|
|
1212
|
+
{
|
|
1213
|
+
code: "LAX",
|
|
1214
|
+
country: "US",
|
|
1215
|
+
ghcnh_id: "USW00023174",
|
|
1216
|
+
icao: "KLAX",
|
|
1217
|
+
latitude: 33.9425,
|
|
1218
|
+
longitude: -118.4081,
|
|
1219
|
+
name: "Los Angeles International",
|
|
1220
|
+
tz: "America/Los_Angeles"
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
code: "MDW",
|
|
1224
|
+
country: "US",
|
|
1225
|
+
ghcnh_id: "USW00014819",
|
|
1226
|
+
icao: "KMDW",
|
|
1227
|
+
latitude: 41.7868,
|
|
1228
|
+
longitude: -87.7522,
|
|
1229
|
+
name: "Chicago Midway International",
|
|
1230
|
+
tz: "America/Chicago"
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
code: "MIA",
|
|
1234
|
+
country: "US",
|
|
1235
|
+
ghcnh_id: "USW00012839",
|
|
1236
|
+
icao: "KMIA",
|
|
1237
|
+
latitude: 25.7959,
|
|
1238
|
+
longitude: -80.287,
|
|
1239
|
+
name: "Miami International",
|
|
1240
|
+
tz: "America/New_York"
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
code: "MSP",
|
|
1244
|
+
country: "US",
|
|
1245
|
+
ghcnh_id: "USW00014922",
|
|
1246
|
+
icao: "KMSP",
|
|
1247
|
+
latitude: 44.8848,
|
|
1248
|
+
longitude: -93.2223,
|
|
1249
|
+
name: "Minneapolis-St Paul International",
|
|
1250
|
+
tz: "America/Chicago"
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
code: "MSY",
|
|
1254
|
+
country: "US",
|
|
1255
|
+
ghcnh_id: "USW00012916",
|
|
1256
|
+
icao: "KMSY",
|
|
1257
|
+
latitude: 29.9934,
|
|
1258
|
+
longitude: -90.258,
|
|
1259
|
+
name: "New Orleans Louis Armstrong International",
|
|
1260
|
+
tz: "America/Chicago"
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
code: "NYC",
|
|
1264
|
+
country: "US",
|
|
1265
|
+
ghcnh_id: "USW00094728",
|
|
1266
|
+
icao: "KNYC",
|
|
1267
|
+
latitude: 40.7789,
|
|
1268
|
+
longitude: -73.9692,
|
|
1269
|
+
name: "Central Park, New York",
|
|
1270
|
+
tz: "America/New_York"
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
code: "OKC",
|
|
1274
|
+
country: "US",
|
|
1275
|
+
ghcnh_id: "USW00013967",
|
|
1276
|
+
icao: "KOKC",
|
|
1277
|
+
latitude: 35.3931,
|
|
1278
|
+
longitude: -97.6007,
|
|
1279
|
+
name: "Oklahoma City Will Rogers World",
|
|
1280
|
+
tz: "America/Chicago"
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
code: "PHL",
|
|
1284
|
+
country: "US",
|
|
1285
|
+
ghcnh_id: "USW00013739",
|
|
1286
|
+
icao: "KPHL",
|
|
1287
|
+
latitude: 39.8721,
|
|
1288
|
+
longitude: -75.2411,
|
|
1289
|
+
name: "Philadelphia International",
|
|
1290
|
+
tz: "America/New_York"
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
code: "PHX",
|
|
1294
|
+
country: "US",
|
|
1295
|
+
ghcnh_id: "USW00023183",
|
|
1296
|
+
icao: "KPHX",
|
|
1297
|
+
latitude: 33.4373,
|
|
1298
|
+
longitude: -112.0078,
|
|
1299
|
+
name: "Phoenix Sky Harbor International",
|
|
1300
|
+
tz: "America/Phoenix"
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
code: "SAT",
|
|
1304
|
+
country: "US",
|
|
1305
|
+
ghcnh_id: "USW00012921",
|
|
1306
|
+
icao: "KSAT",
|
|
1307
|
+
latitude: 29.5337,
|
|
1308
|
+
longitude: -98.4698,
|
|
1309
|
+
name: "San Antonio International",
|
|
1310
|
+
tz: "America/Chicago"
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
code: "SEA",
|
|
1314
|
+
country: "US",
|
|
1315
|
+
ghcnh_id: "USW00024233",
|
|
1316
|
+
icao: "KSEA",
|
|
1317
|
+
latitude: 47.4502,
|
|
1318
|
+
longitude: -122.3088,
|
|
1319
|
+
name: "Seattle-Tacoma International",
|
|
1320
|
+
tz: "America/Los_Angeles"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
code: "SFO",
|
|
1324
|
+
country: "US",
|
|
1325
|
+
ghcnh_id: "USW00023234",
|
|
1326
|
+
icao: "KSFO",
|
|
1327
|
+
latitude: 37.6213,
|
|
1328
|
+
longitude: -122.379,
|
|
1329
|
+
name: "San Francisco International",
|
|
1330
|
+
tz: "America/Los_Angeles"
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
code: "LEBL",
|
|
1334
|
+
country: "ES",
|
|
1335
|
+
ghcnh_id: null,
|
|
1336
|
+
icao: "LEBL",
|
|
1337
|
+
latitude: 41.2974,
|
|
1338
|
+
longitude: 2.0833,
|
|
1339
|
+
name: "Barcelona El Prat",
|
|
1340
|
+
tz: "Europe/Madrid"
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
code: "LEMD",
|
|
1344
|
+
country: "ES",
|
|
1345
|
+
ghcnh_id: null,
|
|
1346
|
+
icao: "LEMD",
|
|
1347
|
+
latitude: 40.4719,
|
|
1348
|
+
longitude: -3.5626,
|
|
1349
|
+
name: "Madrid Barajas",
|
|
1350
|
+
tz: "Europe/Madrid"
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
code: "LFPB",
|
|
1354
|
+
country: "FR",
|
|
1355
|
+
ghcnh_id: null,
|
|
1356
|
+
icao: "LFPB",
|
|
1357
|
+
latitude: 48.9694,
|
|
1358
|
+
longitude: 2.4414,
|
|
1359
|
+
name: "Paris Le Bourget",
|
|
1360
|
+
tz: "Europe/Paris"
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
code: "LFPG",
|
|
1364
|
+
country: "FR",
|
|
1365
|
+
ghcnh_id: null,
|
|
1366
|
+
icao: "LFPG",
|
|
1367
|
+
latitude: 49.0097,
|
|
1368
|
+
longitude: 2.5479,
|
|
1369
|
+
name: "Paris Charles de Gaulle",
|
|
1370
|
+
tz: "Europe/Paris"
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
code: "LFPO",
|
|
1374
|
+
country: "FR",
|
|
1375
|
+
ghcnh_id: null,
|
|
1376
|
+
icao: "LFPO",
|
|
1377
|
+
latitude: 48.7233,
|
|
1378
|
+
longitude: 2.3794,
|
|
1379
|
+
name: "Paris Orly",
|
|
1380
|
+
tz: "Europe/Paris"
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
code: "LIMC",
|
|
1384
|
+
country: "IT",
|
|
1385
|
+
ghcnh_id: null,
|
|
1386
|
+
icao: "LIMC",
|
|
1387
|
+
latitude: 45.6306,
|
|
1388
|
+
longitude: 8.7281,
|
|
1389
|
+
name: "Milan Malpensa",
|
|
1390
|
+
tz: "Europe/Rome"
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
code: "LIRF",
|
|
1394
|
+
country: "IT",
|
|
1395
|
+
ghcnh_id: null,
|
|
1396
|
+
icao: "LIRF",
|
|
1397
|
+
latitude: 41.8003,
|
|
1398
|
+
longitude: 12.2389,
|
|
1399
|
+
name: "Rome Fiumicino",
|
|
1400
|
+
tz: "Europe/Rome"
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
code: "LOWW",
|
|
1404
|
+
country: "AT",
|
|
1405
|
+
ghcnh_id: null,
|
|
1406
|
+
icao: "LOWW",
|
|
1407
|
+
latitude: 48.1103,
|
|
1408
|
+
longitude: 16.5697,
|
|
1409
|
+
name: "Vienna International",
|
|
1410
|
+
tz: "Europe/Vienna"
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
code: "LSZH",
|
|
1414
|
+
country: "CH",
|
|
1415
|
+
ghcnh_id: null,
|
|
1416
|
+
icao: "LSZH",
|
|
1417
|
+
latitude: 47.4647,
|
|
1418
|
+
longitude: 8.5492,
|
|
1419
|
+
name: "Zurich",
|
|
1420
|
+
tz: "Europe/Zurich"
|
|
1421
|
+
},
|
|
1422
|
+
{
|
|
1423
|
+
code: "NZAA",
|
|
1424
|
+
country: "NZ",
|
|
1425
|
+
ghcnh_id: null,
|
|
1426
|
+
icao: "NZAA",
|
|
1427
|
+
latitude: -37.0081,
|
|
1428
|
+
longitude: 174.7917,
|
|
1429
|
+
name: "Auckland",
|
|
1430
|
+
tz: "Pacific/Auckland"
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
code: "NZWN",
|
|
1434
|
+
country: "NZ",
|
|
1435
|
+
ghcnh_id: null,
|
|
1436
|
+
icao: "NZWN",
|
|
1437
|
+
latitude: -41.3272,
|
|
1438
|
+
longitude: 174.8053,
|
|
1439
|
+
name: "Wellington",
|
|
1440
|
+
tz: "Pacific/Auckland"
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
code: "OERK",
|
|
1444
|
+
country: "SA",
|
|
1445
|
+
ghcnh_id: null,
|
|
1446
|
+
icao: "OERK",
|
|
1447
|
+
latitude: 24.9576,
|
|
1448
|
+
longitude: 46.6988,
|
|
1449
|
+
name: "Riyadh King Khalid International",
|
|
1450
|
+
tz: "Asia/Riyadh"
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
code: "OMDB",
|
|
1454
|
+
country: "AE",
|
|
1455
|
+
ghcnh_id: null,
|
|
1456
|
+
icao: "OMDB",
|
|
1457
|
+
latitude: 25.2532,
|
|
1458
|
+
longitude: 55.3657,
|
|
1459
|
+
name: "Dubai International",
|
|
1460
|
+
tz: "Asia/Dubai"
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
code: "OTHH",
|
|
1464
|
+
country: "QA",
|
|
1465
|
+
ghcnh_id: null,
|
|
1466
|
+
icao: "OTHH",
|
|
1467
|
+
latitude: 25.2731,
|
|
1468
|
+
longitude: 51.608,
|
|
1469
|
+
name: "Doha Hamad International",
|
|
1470
|
+
tz: "Asia/Qatar"
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
code: "RCTP",
|
|
1474
|
+
country: "TW",
|
|
1475
|
+
ghcnh_id: null,
|
|
1476
|
+
icao: "RCTP",
|
|
1477
|
+
latitude: 25.0777,
|
|
1478
|
+
longitude: 121.2328,
|
|
1479
|
+
name: "Taipei Taoyuan",
|
|
1480
|
+
tz: "Asia/Taipei"
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
code: "RJAA",
|
|
1484
|
+
country: "JP",
|
|
1485
|
+
ghcnh_id: null,
|
|
1486
|
+
icao: "RJAA",
|
|
1487
|
+
latitude: 35.7647,
|
|
1488
|
+
longitude: 140.3864,
|
|
1489
|
+
name: "Tokyo Narita",
|
|
1490
|
+
tz: "Asia/Tokyo"
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
code: "RJTT",
|
|
1494
|
+
country: "JP",
|
|
1495
|
+
ghcnh_id: null,
|
|
1496
|
+
icao: "RJTT",
|
|
1497
|
+
latitude: 35.5522,
|
|
1498
|
+
longitude: 139.78,
|
|
1499
|
+
name: "Tokyo Haneda",
|
|
1500
|
+
tz: "Asia/Tokyo"
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
code: "RKSI",
|
|
1504
|
+
country: "KR",
|
|
1505
|
+
ghcnh_id: null,
|
|
1506
|
+
icao: "RKSI",
|
|
1507
|
+
latitude: 37.4691,
|
|
1508
|
+
longitude: 126.4505,
|
|
1509
|
+
name: "Seoul Incheon",
|
|
1510
|
+
tz: "Asia/Seoul"
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
code: "SAEZ",
|
|
1514
|
+
country: "AR",
|
|
1515
|
+
ghcnh_id: null,
|
|
1516
|
+
icao: "SAEZ",
|
|
1517
|
+
latitude: -34.8222,
|
|
1518
|
+
longitude: -58.5358,
|
|
1519
|
+
name: "Buenos Aires Ezeiza",
|
|
1520
|
+
tz: "America/Argentina/Buenos_Aires"
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
code: "SBGR",
|
|
1524
|
+
country: "BR",
|
|
1525
|
+
ghcnh_id: null,
|
|
1526
|
+
icao: "SBGR",
|
|
1527
|
+
latitude: -23.4356,
|
|
1528
|
+
longitude: -46.4731,
|
|
1529
|
+
name: "S\xE3o Paulo Guarulhos",
|
|
1530
|
+
tz: "America/Sao_Paulo"
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
code: "UUEE",
|
|
1534
|
+
country: "RU",
|
|
1535
|
+
ghcnh_id: null,
|
|
1536
|
+
icao: "UUEE",
|
|
1537
|
+
latitude: 55.9728,
|
|
1538
|
+
longitude: 37.4147,
|
|
1539
|
+
name: "Moscow Sheremetyevo",
|
|
1540
|
+
tz: "Europe/Moscow"
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
code: "VABB",
|
|
1544
|
+
country: "IN",
|
|
1545
|
+
ghcnh_id: null,
|
|
1546
|
+
icao: "VABB",
|
|
1547
|
+
latitude: 19.0887,
|
|
1548
|
+
longitude: 72.8679,
|
|
1549
|
+
name: "Mumbai Chhatrapati Shivaji",
|
|
1550
|
+
tz: "Asia/Kolkata"
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
code: "VHHH",
|
|
1554
|
+
country: "HK",
|
|
1555
|
+
ghcnh_id: null,
|
|
1556
|
+
icao: "VHHH",
|
|
1557
|
+
latitude: 22.308,
|
|
1558
|
+
longitude: 113.9185,
|
|
1559
|
+
name: "Hong Kong International",
|
|
1560
|
+
tz: "Asia/Hong_Kong"
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
code: "VIDP",
|
|
1564
|
+
country: "IN",
|
|
1565
|
+
ghcnh_id: null,
|
|
1566
|
+
icao: "VIDP",
|
|
1567
|
+
latitude: 28.5562,
|
|
1568
|
+
longitude: 77.1,
|
|
1569
|
+
name: "Delhi Indira Gandhi",
|
|
1570
|
+
tz: "Asia/Kolkata"
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
code: "VTBS",
|
|
1574
|
+
country: "TH",
|
|
1575
|
+
ghcnh_id: null,
|
|
1576
|
+
icao: "VTBS",
|
|
1577
|
+
latitude: 13.69,
|
|
1578
|
+
longitude: 100.7501,
|
|
1579
|
+
name: "Bangkok Suvarnabhumi",
|
|
1580
|
+
tz: "Asia/Bangkok"
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
code: "WSSS",
|
|
1584
|
+
country: "SG",
|
|
1585
|
+
ghcnh_id: null,
|
|
1586
|
+
icao: "WSSS",
|
|
1587
|
+
latitude: 1.3644,
|
|
1588
|
+
longitude: 103.9915,
|
|
1589
|
+
name: "Singapore Changi",
|
|
1590
|
+
tz: "Asia/Singapore"
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
code: "YBBN",
|
|
1594
|
+
country: "AU",
|
|
1595
|
+
ghcnh_id: null,
|
|
1596
|
+
icao: "YBBN",
|
|
1597
|
+
latitude: -27.3842,
|
|
1598
|
+
longitude: 153.1175,
|
|
1599
|
+
name: "Brisbane",
|
|
1600
|
+
tz: "Australia/Brisbane"
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
code: "YMML",
|
|
1604
|
+
country: "AU",
|
|
1605
|
+
ghcnh_id: null,
|
|
1606
|
+
icao: "YMML",
|
|
1607
|
+
latitude: -37.6733,
|
|
1608
|
+
longitude: 144.8433,
|
|
1609
|
+
name: "Melbourne Tullamarine",
|
|
1610
|
+
tz: "Australia/Melbourne"
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
code: "YSSY",
|
|
1614
|
+
country: "AU",
|
|
1615
|
+
ghcnh_id: null,
|
|
1616
|
+
icao: "YSSY",
|
|
1617
|
+
latitude: -33.9461,
|
|
1618
|
+
longitude: 151.1772,
|
|
1619
|
+
name: "Sydney Kingsford Smith",
|
|
1620
|
+
tz: "Australia/Sydney"
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
code: "ZBAA",
|
|
1624
|
+
country: "CN",
|
|
1625
|
+
ghcnh_id: null,
|
|
1626
|
+
icao: "ZBAA",
|
|
1627
|
+
latitude: 40.0801,
|
|
1628
|
+
longitude: 116.5846,
|
|
1629
|
+
name: "Beijing Capital",
|
|
1630
|
+
tz: "Asia/Shanghai"
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
code: "ZSPD",
|
|
1634
|
+
country: "CN",
|
|
1635
|
+
ghcnh_id: null,
|
|
1636
|
+
icao: "ZSPD",
|
|
1637
|
+
latitude: 31.1443,
|
|
1638
|
+
longitude: 121.8083,
|
|
1639
|
+
name: "Shanghai Pudong",
|
|
1640
|
+
tz: "Asia/Shanghai"
|
|
1641
|
+
}
|
|
1642
|
+
];
|
|
1643
|
+
var STATION_BY_CODE2 = /* @__PURE__ */ new Map([
|
|
1644
|
+
["ATL", STATIONS2[10]],
|
|
1645
|
+
["AUS", STATIONS2[11]],
|
|
1646
|
+
["BOS", STATIONS2[12]],
|
|
1647
|
+
["DCA", STATIONS2[13]],
|
|
1648
|
+
["DEN", STATIONS2[14]],
|
|
1649
|
+
["DFW", STATIONS2[15]],
|
|
1650
|
+
["EDDB", STATIONS2[0]],
|
|
1651
|
+
["EDDF", STATIONS2[1]],
|
|
1652
|
+
["EDDM", STATIONS2[2]],
|
|
1653
|
+
["EFHK", STATIONS2[3]],
|
|
1654
|
+
["EGKK", STATIONS2[4]],
|
|
1655
|
+
["EGLL", STATIONS2[5]],
|
|
1656
|
+
["EHAM", STATIONS2[6]],
|
|
1657
|
+
["EKCH", STATIONS2[7]],
|
|
1658
|
+
["EPWA", STATIONS2[8]],
|
|
1659
|
+
["ESSA", STATIONS2[9]],
|
|
1660
|
+
["HOU", STATIONS2[16]],
|
|
1661
|
+
["LAS", STATIONS2[17]],
|
|
1662
|
+
["LAX", STATIONS2[18]],
|
|
1663
|
+
["LEBL", STATIONS2[30]],
|
|
1664
|
+
["LEMD", STATIONS2[31]],
|
|
1665
|
+
["LFPB", STATIONS2[32]],
|
|
1666
|
+
["LFPG", STATIONS2[33]],
|
|
1667
|
+
["LFPO", STATIONS2[34]],
|
|
1668
|
+
["LIMC", STATIONS2[35]],
|
|
1669
|
+
["LIRF", STATIONS2[36]],
|
|
1670
|
+
["LOWW", STATIONS2[37]],
|
|
1671
|
+
["LSZH", STATIONS2[38]],
|
|
1672
|
+
["MDW", STATIONS2[19]],
|
|
1673
|
+
["MIA", STATIONS2[20]],
|
|
1674
|
+
["MSP", STATIONS2[21]],
|
|
1675
|
+
["MSY", STATIONS2[22]],
|
|
1676
|
+
["NYC", STATIONS2[23]],
|
|
1677
|
+
["NZAA", STATIONS2[39]],
|
|
1678
|
+
["NZWN", STATIONS2[40]],
|
|
1679
|
+
["OERK", STATIONS2[41]],
|
|
1680
|
+
["OKC", STATIONS2[24]],
|
|
1681
|
+
["OMDB", STATIONS2[42]],
|
|
1682
|
+
["OTHH", STATIONS2[43]],
|
|
1683
|
+
["PHL", STATIONS2[25]],
|
|
1684
|
+
["PHX", STATIONS2[26]],
|
|
1685
|
+
["RCTP", STATIONS2[44]],
|
|
1686
|
+
["RJAA", STATIONS2[45]],
|
|
1687
|
+
["RJTT", STATIONS2[46]],
|
|
1688
|
+
["RKSI", STATIONS2[47]],
|
|
1689
|
+
["SAEZ", STATIONS2[48]],
|
|
1690
|
+
["SAT", STATIONS2[27]],
|
|
1691
|
+
["SBGR", STATIONS2[49]],
|
|
1692
|
+
["SEA", STATIONS2[28]],
|
|
1693
|
+
["SFO", STATIONS2[29]],
|
|
1694
|
+
["UUEE", STATIONS2[50]],
|
|
1695
|
+
["VABB", STATIONS2[51]],
|
|
1696
|
+
["VHHH", STATIONS2[52]],
|
|
1697
|
+
["VIDP", STATIONS2[53]],
|
|
1698
|
+
["VTBS", STATIONS2[54]],
|
|
1699
|
+
["WSSS", STATIONS2[55]],
|
|
1700
|
+
["YBBN", STATIONS2[56]],
|
|
1701
|
+
["YMML", STATIONS2[57]],
|
|
1702
|
+
["YSSY", STATIONS2[58]],
|
|
1703
|
+
["ZBAA", STATIONS2[59]],
|
|
1704
|
+
["ZSPD", STATIONS2[60]]
|
|
1705
|
+
]);
|
|
1706
|
+
var STATION_BY_ICAO2 = /* @__PURE__ */ new Map([
|
|
1707
|
+
["EDDB", STATIONS2[0]],
|
|
1708
|
+
["EDDF", STATIONS2[1]],
|
|
1709
|
+
["EDDM", STATIONS2[2]],
|
|
1710
|
+
["EFHK", STATIONS2[3]],
|
|
1711
|
+
["EGKK", STATIONS2[4]],
|
|
1712
|
+
["EGLL", STATIONS2[5]],
|
|
1713
|
+
["EHAM", STATIONS2[6]],
|
|
1714
|
+
["EKCH", STATIONS2[7]],
|
|
1715
|
+
["EPWA", STATIONS2[8]],
|
|
1716
|
+
["ESSA", STATIONS2[9]],
|
|
1717
|
+
["KATL", STATIONS2[10]],
|
|
1718
|
+
["KAUS", STATIONS2[11]],
|
|
1719
|
+
["KBOS", STATIONS2[12]],
|
|
1720
|
+
["KDCA", STATIONS2[13]],
|
|
1721
|
+
["KDEN", STATIONS2[14]],
|
|
1722
|
+
["KDFW", STATIONS2[15]],
|
|
1723
|
+
["KHOU", STATIONS2[16]],
|
|
1724
|
+
["KLAS", STATIONS2[17]],
|
|
1725
|
+
["KLAX", STATIONS2[18]],
|
|
1726
|
+
["KMDW", STATIONS2[19]],
|
|
1727
|
+
["KMIA", STATIONS2[20]],
|
|
1728
|
+
["KMSP", STATIONS2[21]],
|
|
1729
|
+
["KMSY", STATIONS2[22]],
|
|
1730
|
+
["KNYC", STATIONS2[23]],
|
|
1731
|
+
["KOKC", STATIONS2[24]],
|
|
1732
|
+
["KPHL", STATIONS2[25]],
|
|
1733
|
+
["KPHX", STATIONS2[26]],
|
|
1734
|
+
["KSAT", STATIONS2[27]],
|
|
1735
|
+
["KSEA", STATIONS2[28]],
|
|
1736
|
+
["KSFO", STATIONS2[29]],
|
|
1737
|
+
["LEBL", STATIONS2[30]],
|
|
1738
|
+
["LEMD", STATIONS2[31]],
|
|
1739
|
+
["LFPB", STATIONS2[32]],
|
|
1740
|
+
["LFPG", STATIONS2[33]],
|
|
1741
|
+
["LFPO", STATIONS2[34]],
|
|
1742
|
+
["LIMC", STATIONS2[35]],
|
|
1743
|
+
["LIRF", STATIONS2[36]],
|
|
1744
|
+
["LOWW", STATIONS2[37]],
|
|
1745
|
+
["LSZH", STATIONS2[38]],
|
|
1746
|
+
["NZAA", STATIONS2[39]],
|
|
1747
|
+
["NZWN", STATIONS2[40]],
|
|
1748
|
+
["OERK", STATIONS2[41]],
|
|
1749
|
+
["OMDB", STATIONS2[42]],
|
|
1750
|
+
["OTHH", STATIONS2[43]],
|
|
1751
|
+
["RCTP", STATIONS2[44]],
|
|
1752
|
+
["RJAA", STATIONS2[45]],
|
|
1753
|
+
["RJTT", STATIONS2[46]],
|
|
1754
|
+
["RKSI", STATIONS2[47]],
|
|
1755
|
+
["SAEZ", STATIONS2[48]],
|
|
1756
|
+
["SBGR", STATIONS2[49]],
|
|
1757
|
+
["UUEE", STATIONS2[50]],
|
|
1758
|
+
["VABB", STATIONS2[51]],
|
|
1759
|
+
["VHHH", STATIONS2[52]],
|
|
1760
|
+
["VIDP", STATIONS2[53]],
|
|
1761
|
+
["VTBS", STATIONS2[54]],
|
|
1762
|
+
["WSSS", STATIONS2[55]],
|
|
1763
|
+
["YBBN", STATIONS2[56]],
|
|
1764
|
+
["YMML", STATIONS2[57]],
|
|
1765
|
+
["YSSY", STATIONS2[58]],
|
|
1766
|
+
["ZBAA", STATIONS2[59]],
|
|
1767
|
+
["ZSPD", STATIONS2[60]]
|
|
1768
|
+
]);
|
|
1769
|
+
var LOW_COVERAGE_THRESHOLD = 12;
|
|
1770
|
+
var PARTS_CACHE = /* @__PURE__ */ new Map();
|
|
1771
|
+
function getDateFormatter(tz) {
|
|
1772
|
+
let f = PARTS_CACHE.get(tz);
|
|
1773
|
+
if (f === void 0) {
|
|
1774
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
1775
|
+
timeZone: tz,
|
|
1776
|
+
year: "numeric",
|
|
1777
|
+
month: "2-digit",
|
|
1778
|
+
day: "2-digit"
|
|
1779
|
+
});
|
|
1780
|
+
PARTS_CACHE.set(tz, f);
|
|
1781
|
+
}
|
|
1782
|
+
return f;
|
|
1783
|
+
}
|
|
1784
|
+
function localDateFor(instant, tz) {
|
|
1785
|
+
const parts = getDateFormatter(tz).formatToParts(instant);
|
|
1786
|
+
let y = "";
|
|
1787
|
+
let m = "";
|
|
1788
|
+
let d = "";
|
|
1789
|
+
for (const p of parts) {
|
|
1790
|
+
if (p.type === "year") y = p.value;
|
|
1791
|
+
else if (p.type === "month") m = p.value;
|
|
1792
|
+
else if (p.type === "day") d = p.value;
|
|
1793
|
+
}
|
|
1794
|
+
return `${y}-${m}-${d}`;
|
|
1795
|
+
}
|
|
1796
|
+
function parseInstant(observed) {
|
|
1797
|
+
if (observed === void 0 || observed === null || observed.length === 0) {
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
const ms = Date.parse(observed);
|
|
1801
|
+
if (Number.isNaN(ms)) return null;
|
|
1802
|
+
return new Date(ms);
|
|
1803
|
+
}
|
|
1804
|
+
function roundHalfUp(value, places) {
|
|
1805
|
+
if (!Number.isFinite(value)) return value;
|
|
1806
|
+
const scale = 10 ** places;
|
|
1807
|
+
const sign = value < 0 ? -1 : 1;
|
|
1808
|
+
const abs = Math.abs(value);
|
|
1809
|
+
const rounded = Math.floor(abs * scale + 0.5 + abs * 1e-12) / scale;
|
|
1810
|
+
return sign * rounded;
|
|
1811
|
+
}
|
|
1812
|
+
function cToF(c) {
|
|
1813
|
+
return c * 1.8 + 32;
|
|
1814
|
+
}
|
|
1815
|
+
function internationalDailyExtremes(rows, opts) {
|
|
1816
|
+
const tz = opts.stationTz;
|
|
1817
|
+
if (typeof tz !== "string" || tz.length === 0) {
|
|
1818
|
+
throw new RangeError("internationalDailyExtremes: stationTz is required (non-empty string)");
|
|
1819
|
+
}
|
|
1820
|
+
const precision = opts.precision ?? 0;
|
|
1821
|
+
const minObs = opts.minObs ?? LOW_COVERAGE_THRESHOLD;
|
|
1822
|
+
try {
|
|
1823
|
+
getDateFormatter(tz);
|
|
1824
|
+
} catch (e) {
|
|
1825
|
+
throw new RangeError(
|
|
1826
|
+
`internationalDailyExtremes: invalid stationTz ${JSON.stringify(tz)}: ${e.message}`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
const byLocalDate = /* @__PURE__ */ new Map();
|
|
1830
|
+
for (const row of rows) {
|
|
1831
|
+
const instant = parseInstant(row.observed_at);
|
|
1832
|
+
if (instant === null) continue;
|
|
1833
|
+
const localDate = localDateFor(instant, tz);
|
|
1834
|
+
let bucket = byLocalDate.get(localDate);
|
|
1835
|
+
if (bucket === void 0) {
|
|
1836
|
+
bucket = { temps: [], precipMm: 0 };
|
|
1837
|
+
byLocalDate.set(localDate, bucket);
|
|
1838
|
+
}
|
|
1839
|
+
const t = row.temp_c;
|
|
1840
|
+
if (typeof t === "number" && Number.isFinite(t)) {
|
|
1841
|
+
bucket.temps.push({ value: t, source: row.source ?? null });
|
|
1842
|
+
}
|
|
1843
|
+
const p = row.precip_mm_1h;
|
|
1844
|
+
if (typeof p === "number" && Number.isFinite(p)) {
|
|
1845
|
+
bucket.precipMm += p;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const out = [];
|
|
1849
|
+
const sortedDates = [...byLocalDate.keys()].sort();
|
|
1850
|
+
for (const localDate of sortedDates) {
|
|
1851
|
+
const bucket = byLocalDate.get(localDate);
|
|
1852
|
+
if (bucket === void 0) continue;
|
|
1853
|
+
const nObs = bucket.temps.length;
|
|
1854
|
+
let tempMinC = null;
|
|
1855
|
+
let tempMaxC = null;
|
|
1856
|
+
let tempMeanC = null;
|
|
1857
|
+
let sourceTmin = null;
|
|
1858
|
+
let sourceTmax = null;
|
|
1859
|
+
if (nObs > 0 && nObs >= minObs) {
|
|
1860
|
+
let minIdx = 0;
|
|
1861
|
+
let maxIdx = 0;
|
|
1862
|
+
let sum = 0;
|
|
1863
|
+
for (let i = 0; i < bucket.temps.length; i += 1) {
|
|
1864
|
+
const v = bucket.temps[i];
|
|
1865
|
+
sum += v.value;
|
|
1866
|
+
const minRow2 = bucket.temps[minIdx];
|
|
1867
|
+
const maxRow2 = bucket.temps[maxIdx];
|
|
1868
|
+
if (v.value < minRow2.value) minIdx = i;
|
|
1869
|
+
if (v.value > maxRow2.value) maxIdx = i;
|
|
1870
|
+
}
|
|
1871
|
+
const mean = sum / nObs;
|
|
1872
|
+
const minRow = bucket.temps[minIdx];
|
|
1873
|
+
const maxRow = bucket.temps[maxIdx];
|
|
1874
|
+
tempMinC = roundHalfUp(minRow.value, precision);
|
|
1875
|
+
tempMaxC = roundHalfUp(maxRow.value, precision);
|
|
1876
|
+
tempMeanC = roundHalfUp(mean, precision);
|
|
1877
|
+
sourceTmin = minRow.source;
|
|
1878
|
+
sourceTmax = maxRow.source;
|
|
1879
|
+
}
|
|
1880
|
+
out.push(
|
|
1881
|
+
Object.freeze({
|
|
1882
|
+
localDate,
|
|
1883
|
+
nObs,
|
|
1884
|
+
tempMinC,
|
|
1885
|
+
tempMaxC,
|
|
1886
|
+
tempMeanC,
|
|
1887
|
+
tempMinF: tempMinC === null ? null : roundHalfUp(cToF(tempMinC), precision),
|
|
1888
|
+
tempMaxF: tempMaxC === null ? null : roundHalfUp(cToF(tempMaxC), precision),
|
|
1889
|
+
precipMm: roundHalfUp(bucket.precipMm, 4),
|
|
1890
|
+
sourceTmin,
|
|
1891
|
+
sourceTmax
|
|
1892
|
+
})
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
return out;
|
|
1896
|
+
}
|
|
1897
|
+
var BUILT_IN_SCHEMAS = Object.freeze([
|
|
1898
|
+
{
|
|
1899
|
+
id: "schema.observation.v1",
|
|
1900
|
+
title: "schema.observation.v1",
|
|
1901
|
+
columnCount: 20,
|
|
1902
|
+
columns: [
|
|
1903
|
+
{ name: "dew_point_c", description: "units: celsius \u2014 bounded", nullable: true },
|
|
1904
|
+
{ name: "event_time", description: "observation valid time", nullable: false },
|
|
1905
|
+
{
|
|
1906
|
+
name: "metar_raw",
|
|
1907
|
+
description: "raw METAR text if source has it; null for AWC JSON (structured-only)",
|
|
1908
|
+
nullable: true
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
name: "observation_type",
|
|
1912
|
+
description: "METAR | SPECI; defaults METAR when source can't distinguish (e.g. AWC JSON)",
|
|
1913
|
+
nullable: false
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
name: "precip_mm_1h",
|
|
1917
|
+
description: "units: mm \u2014 hourly precip (METAR p01i, converted from inches)",
|
|
1918
|
+
nullable: true
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
name: "sky_base_1_m",
|
|
1922
|
+
description: "units: meters \u2014 first cloud layer base height (converted from feet)",
|
|
1923
|
+
nullable: true
|
|
1924
|
+
},
|
|
1925
|
+
{ name: "sky_base_2_m", description: "units: meters", nullable: true },
|
|
1926
|
+
{ name: "sky_base_3_m", description: "units: meters", nullable: true },
|
|
1927
|
+
{ name: "sky_base_4_m", description: "units: meters", nullable: true },
|
|
1928
|
+
{ name: "sky_cover_1", description: "first cloud layer cover code", nullable: true },
|
|
1929
|
+
{ name: "sky_cover_2", description: "second layer; null if not present", nullable: true },
|
|
1930
|
+
{ name: "sky_cover_3", description: "third layer; null if not present", nullable: true },
|
|
1931
|
+
{ name: "sky_cover_4", description: "fourth layer; null if not present", nullable: true },
|
|
1932
|
+
{
|
|
1933
|
+
name: "slp_hpa",
|
|
1934
|
+
description: "units: hPa \u2014 sea-level pressure (canonical aviation unit, not converted across modes)",
|
|
1935
|
+
nullable: true
|
|
1936
|
+
},
|
|
1937
|
+
{ name: "station", description: "ICAO/ASOS station ID (e.g. KORD)", nullable: false },
|
|
1938
|
+
{
|
|
1939
|
+
name: "temp_c",
|
|
1940
|
+
description: "units: celsius \u2014 bounded TEMP_MIN_C..TEMP_MAX_C",
|
|
1941
|
+
nullable: true
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
name: "visibility_m",
|
|
1945
|
+
description: "units: meters \u2014 converted from statute miles",
|
|
1946
|
+
nullable: true
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
name: "wind_dir_deg",
|
|
1950
|
+
description: "units: degrees \u2014 0-360, bounded",
|
|
1951
|
+
nullable: true
|
|
1952
|
+
},
|
|
1953
|
+
{ name: "wind_gust_ms", description: "units: m/s \u2014 converted from kt", nullable: true },
|
|
1954
|
+
{ name: "wind_speed_ms", description: "units: m/s \u2014 converted from kt", nullable: true }
|
|
1955
|
+
]
|
|
1956
|
+
},
|
|
1957
|
+
{
|
|
1958
|
+
id: "schema.forecast.iem_mos.v1",
|
|
1959
|
+
title: "schema.forecast.iem_mos.v1",
|
|
1960
|
+
columnCount: 11,
|
|
1961
|
+
columns: [
|
|
1962
|
+
{ name: "dew_point_c", description: "units: celsius", nullable: true },
|
|
1963
|
+
{
|
|
1964
|
+
name: "forecast_hour",
|
|
1965
|
+
description: "units: hours \u2014 (valid_at - issued_at).total_seconds() / 3600",
|
|
1966
|
+
nullable: false
|
|
1967
|
+
},
|
|
1968
|
+
{
|
|
1969
|
+
name: "issued_at",
|
|
1970
|
+
description: "model run time (from source `runtime` field)",
|
|
1971
|
+
nullable: false
|
|
1972
|
+
},
|
|
1973
|
+
{ name: "model", description: "e.g. NBE, GFS, LAV, MET", nullable: false },
|
|
1974
|
+
{
|
|
1975
|
+
name: "precip_probability",
|
|
1976
|
+
description: "units: probability \u2014 bounded [0, 1]",
|
|
1977
|
+
nullable: true
|
|
1978
|
+
},
|
|
1979
|
+
{
|
|
1980
|
+
name: "sky_cover_pct",
|
|
1981
|
+
description: "units: percent \u2014 bounded [0, 100]",
|
|
1982
|
+
nullable: true
|
|
1983
|
+
},
|
|
1984
|
+
{ name: "station", description: "", nullable: false },
|
|
1985
|
+
{ name: "temp_c", description: "units: celsius", nullable: true },
|
|
1986
|
+
{
|
|
1987
|
+
name: "valid_at",
|
|
1988
|
+
description: "forecast target time (from source `ftime`)",
|
|
1989
|
+
nullable: false
|
|
1990
|
+
},
|
|
1991
|
+
{ name: "wind_dir_deg", description: "units: degrees", nullable: true },
|
|
1992
|
+
{ name: "wind_speed_ms", description: "units: m/s", nullable: true }
|
|
1993
|
+
]
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
id: "schema.settlement.cli.v1",
|
|
1997
|
+
title: "schema.settlement.cli.v1",
|
|
1998
|
+
columnCount: 12,
|
|
1999
|
+
columns: [
|
|
2000
|
+
{
|
|
2001
|
+
name: "cli_data_quality",
|
|
2002
|
+
description: "NWS CLI data-quality marker (Pitfall 6/16). Allows downstream code to filter or weight settlement rows by issuer quality without re-parsing the product header.",
|
|
2003
|
+
nullable: false
|
|
2004
|
+
},
|
|
2005
|
+
{
|
|
2006
|
+
name: "event_time",
|
|
2007
|
+
description: "00:00 local time on observation_date converted to UTC; for sort/join only",
|
|
2008
|
+
nullable: false
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
name: "observation_date",
|
|
2012
|
+
description: "local climate day per NWS convention (no timezone applied to the date itself)",
|
|
2013
|
+
nullable: false
|
|
2014
|
+
},
|
|
2015
|
+
{ name: "precipitation_in", description: "units: inches", nullable: true },
|
|
2016
|
+
{
|
|
2017
|
+
name: "product_release_time",
|
|
2018
|
+
description: "parsed from CLI product header (_climate.py::_parse_product_timestamp)",
|
|
2019
|
+
nullable: false
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
name: "report_type",
|
|
2023
|
+
description: "preliminary | final | correction; dedup priority preliminary < final < correction",
|
|
2024
|
+
nullable: false
|
|
2025
|
+
},
|
|
2026
|
+
{
|
|
2027
|
+
name: "settlement_finality",
|
|
2028
|
+
description: "provisional | final | superseded. Kalshi NHIGH/NLOW settlement contractually requires 'final'; 'provisional' values are kept for early-look research only.",
|
|
2029
|
+
nullable: false
|
|
2030
|
+
},
|
|
2031
|
+
{ name: "snowfall_in", description: "units: inches", nullable: true },
|
|
2032
|
+
{ name: "station", description: "ICAO/ASOS station ID", nullable: false },
|
|
2033
|
+
{
|
|
2034
|
+
name: "station_tz",
|
|
2035
|
+
description: "IANA timezone for the station (e.g. America/Chicago for KORD). Required for local-climate-day semantics; see \xA7U.",
|
|
2036
|
+
nullable: false
|
|
2037
|
+
},
|
|
2038
|
+
{
|
|
2039
|
+
name: "temp_max_F",
|
|
2040
|
+
description: "units: fahrenheit \u2014 daily high (uppercase F for consistency with obs imperial mode)",
|
|
2041
|
+
nullable: true
|
|
2042
|
+
},
|
|
2043
|
+
{ name: "temp_min_F", description: "units: fahrenheit \u2014 daily low", nullable: true }
|
|
2044
|
+
]
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
id: "schema.observation_ledger.v1",
|
|
2048
|
+
title: "schema.observation_ledger.v1",
|
|
2049
|
+
columnCount: 15,
|
|
2050
|
+
columns: [
|
|
2051
|
+
{ name: "as_of_time", description: "", nullable: true },
|
|
2052
|
+
{ name: "dewpoint_c", description: "units: celsius", nullable: true },
|
|
2053
|
+
{ name: "ingestion_id", description: "", nullable: true },
|
|
2054
|
+
{ name: "observation_kind", description: "", nullable: true },
|
|
2055
|
+
{
|
|
2056
|
+
name: "observation_quality",
|
|
2057
|
+
description: "Lineage row-quality flag per LINEAGE-01; distinct from qc_status enum slot AND distinct from the obs_qc_status bitmask column per QC-05.",
|
|
2058
|
+
nullable: true
|
|
2059
|
+
},
|
|
2060
|
+
{ name: "observation_type", description: "", nullable: false },
|
|
2061
|
+
{ name: "observed_at", description: "", nullable: false },
|
|
2062
|
+
{ name: "parser_name", description: "", nullable: true },
|
|
2063
|
+
{ name: "parser_version", description: "", nullable: true },
|
|
2064
|
+
{ name: "provenance", description: "", nullable: true },
|
|
2065
|
+
{ name: "qc_status", description: "", nullable: true },
|
|
2066
|
+
{
|
|
2067
|
+
name: "source",
|
|
2068
|
+
description: "ncei reserved per D-2.1-09; never written in v0.1.0.",
|
|
2069
|
+
nullable: false
|
|
2070
|
+
},
|
|
2071
|
+
{ name: "source_received_at", description: "", nullable: true },
|
|
2072
|
+
{ name: "station_code", description: "", nullable: false },
|
|
2073
|
+
{ name: "temp_c", description: "units: celsius", nullable: true }
|
|
2074
|
+
]
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
id: "schema.observation_qc.v1",
|
|
2078
|
+
title: "schema.observation_qc.v1",
|
|
2079
|
+
columnCount: 13,
|
|
2080
|
+
columns: [
|
|
2081
|
+
{ name: "as_of_time", description: "", nullable: true },
|
|
2082
|
+
{
|
|
2083
|
+
name: "detector_metadata",
|
|
2084
|
+
description: "JSON-serialized detector payload; shape per qc_system.",
|
|
2085
|
+
nullable: true
|
|
2086
|
+
},
|
|
2087
|
+
{
|
|
2088
|
+
name: "field",
|
|
2089
|
+
description: "Observation column the rule evaluated (e.g. temp_c).",
|
|
2090
|
+
nullable: false
|
|
2091
|
+
},
|
|
2092
|
+
{ name: "flag", description: "", nullable: false },
|
|
2093
|
+
{ name: "ingestion_id", description: "", nullable: true },
|
|
2094
|
+
{ name: "observation_kind", description: "", nullable: true },
|
|
2095
|
+
{ name: "observed_at", description: "", nullable: false },
|
|
2096
|
+
{ name: "parser_name", description: "", nullable: true },
|
|
2097
|
+
{ name: "qc_system", description: "", nullable: false },
|
|
2098
|
+
{ name: "qc_version", description: "", nullable: false },
|
|
2099
|
+
{ name: "rule_id", description: "", nullable: false },
|
|
2100
|
+
{ name: "source", description: "", nullable: false },
|
|
2101
|
+
{ name: "station_code", description: "", nullable: false }
|
|
2102
|
+
]
|
|
2103
|
+
}
|
|
2104
|
+
]);
|
|
2105
|
+
function deepFreezeSchema(info) {
|
|
2106
|
+
const frozenCols = Object.freeze(info.columns.map((c) => Object.freeze({ ...c })));
|
|
2107
|
+
return Object.freeze({ ...info, columns: frozenCols });
|
|
2108
|
+
}
|
|
2109
|
+
var REGISTRY = new Map(
|
|
2110
|
+
BUILT_IN_SCHEMAS.map((info) => [info.id, deepFreezeSchema(info)])
|
|
2111
|
+
);
|
|
2112
|
+
var FEATURE_NAMES = Object.freeze([
|
|
2113
|
+
"calendarFeatures",
|
|
2114
|
+
"clipOutliers",
|
|
2115
|
+
"diff",
|
|
2116
|
+
"diff2",
|
|
2117
|
+
"heatIndex",
|
|
2118
|
+
"lag",
|
|
2119
|
+
"rolling",
|
|
2120
|
+
"spread",
|
|
2121
|
+
"windChill"
|
|
2122
|
+
]);
|
|
2123
|
+
|
|
2124
|
+
// ../weather/src/dailyExtremes.ts
|
|
2125
|
+
var LOW_COVERAGE_THRESHOLD2 = 12;
|
|
2126
|
+
function addUtcDays(iso, days) {
|
|
2127
|
+
const [yStr, mStr, dStr] = iso.split("-");
|
|
2128
|
+
const dt = new Date(Date.UTC(Number(yStr), Number(mStr) - 1, Number(dStr)));
|
|
2129
|
+
dt.setUTCDate(dt.getUTCDate() + days);
|
|
2130
|
+
const yyyy = dt.getUTCFullYear();
|
|
2131
|
+
const mm = String(dt.getUTCMonth() + 1).padStart(2, "0");
|
|
2132
|
+
const dd = String(dt.getUTCDate()).padStart(2, "0");
|
|
2133
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
2134
|
+
}
|
|
2135
|
+
function lookupStation(icao) {
|
|
2136
|
+
const upper = icao.toUpperCase();
|
|
2137
|
+
for (const s of STATIONS) {
|
|
2138
|
+
if (s.icao === upper) {
|
|
2139
|
+
return { tz: s.tz, isUs: s.country === "US" };
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
throw new Error(`dailyExtremes: station "${icao}" not in registry \u2014 check STATIONS catalog`);
|
|
2143
|
+
}
|
|
2144
|
+
function cToF2(c) {
|
|
2145
|
+
if (c === null) return null;
|
|
2146
|
+
return c * (9 / 5) + 32;
|
|
2147
|
+
}
|
|
2148
|
+
function roundHalfUp2(value, decimals) {
|
|
2149
|
+
const m = 10 ** decimals;
|
|
2150
|
+
return Math.round(value * m) / m;
|
|
2151
|
+
}
|
|
2152
|
+
async function fetchIemAsosObservations(station, fromDate, toDate) {
|
|
2153
|
+
const fromYear = Number.parseInt(fromDate.slice(0, 4), 10);
|
|
2154
|
+
const toYear = Number.parseInt(toDate.slice(0, 4), 10);
|
|
2155
|
+
const out = [];
|
|
2156
|
+
for (let year = fromYear; year <= toYear; year++) {
|
|
2157
|
+
const chunks = await downloadIemAsos(station, `${year}-01-01`, `${year}-12-31`, {
|
|
2158
|
+
reportType: 3,
|
|
2159
|
+
politenessMs: 1e3
|
|
2160
|
+
});
|
|
2161
|
+
for (const chunk of chunks) {
|
|
2162
|
+
const parsed = parseIemCsv(chunk.csv, { observationTypeOverride: "METAR" });
|
|
2163
|
+
for (const row of parsed) {
|
|
2164
|
+
const obsDate = row.observed_at.slice(0, 10);
|
|
2165
|
+
if (obsDate >= fromDate && obsDate <= toDate) {
|
|
2166
|
+
const precipInches = row.precip_1hr_inches ?? null;
|
|
2167
|
+
out.push({
|
|
2168
|
+
observed_at: row.observed_at,
|
|
2169
|
+
temp_c: row.temp_c ?? null,
|
|
2170
|
+
precip_mm_1h: precipInches !== null ? precipInches * 25.4 : null,
|
|
2171
|
+
source: row.source
|
|
2172
|
+
});
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
return out;
|
|
2178
|
+
}
|
|
2179
|
+
async function fetchAwcObservations(station, fromDate, toDate) {
|
|
2180
|
+
const raw = await fetchAwcMetars([station]);
|
|
2181
|
+
const out = [];
|
|
2182
|
+
for (const r of raw) {
|
|
2183
|
+
const obs2 = awcToObservation(r);
|
|
2184
|
+
if (obs2 === null) continue;
|
|
2185
|
+
const obsDate = obs2.observed_at.slice(0, 10);
|
|
2186
|
+
if (obsDate >= fromDate && obsDate <= toDate) {
|
|
2187
|
+
const precipInches = obs2.precip_1hr_inches ?? null;
|
|
2188
|
+
out.push({
|
|
2189
|
+
observed_at: obs2.observed_at,
|
|
2190
|
+
temp_c: obs2.temp_c ?? null,
|
|
2191
|
+
precip_mm_1h: precipInches !== null ? precipInches * 25.4 : null,
|
|
2192
|
+
source: obs2.source
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return out;
|
|
2197
|
+
}
|
|
2198
|
+
async function fetchForMode(station, fromDate, toDate, mode) {
|
|
2199
|
+
switch (mode) {
|
|
2200
|
+
case "iem_only":
|
|
2201
|
+
return fetchIemAsosObservations(station, fromDate, toDate);
|
|
2202
|
+
case "awc_only":
|
|
2203
|
+
return fetchAwcObservations(station, fromDate, toDate);
|
|
2204
|
+
case "live_v1": {
|
|
2205
|
+
const [iem, awc] = await Promise.all([
|
|
2206
|
+
fetchIemAsosObservations(station, fromDate, toDate),
|
|
2207
|
+
fetchAwcObservations(station, fromDate, toDate).catch(() => [])
|
|
2208
|
+
]);
|
|
2209
|
+
return [...iem, ...awc];
|
|
2210
|
+
}
|
|
2211
|
+
default: {
|
|
2212
|
+
const _exhaustive = mode;
|
|
2213
|
+
throw new TypeError(`dailyExtremes: unknown merge mode "${String(_exhaustive)}"`);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
function projectRow(station, d, isUs) {
|
|
2218
|
+
const lowCoverage = d.nObs < LOW_COVERAGE_THRESHOLD2;
|
|
2219
|
+
const decimals = isUs ? 0 : 1;
|
|
2220
|
+
const precipIn = d.precipMm !== null && d.precipMm !== void 0 ? roundHalfUp2(d.precipMm / 25.4, 2) : null;
|
|
2221
|
+
if (lowCoverage) {
|
|
2222
|
+
return {
|
|
2223
|
+
date: d.localDate,
|
|
2224
|
+
station,
|
|
2225
|
+
tmin_f: null,
|
|
2226
|
+
tmax_f: null,
|
|
2227
|
+
tmean_f: null,
|
|
2228
|
+
precip_in: precipIn,
|
|
2229
|
+
low_coverage: true,
|
|
2230
|
+
n_obs: d.nObs
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
return {
|
|
2234
|
+
date: d.localDate,
|
|
2235
|
+
station,
|
|
2236
|
+
tmin_f: d.tempMinF !== null ? roundHalfUp2(d.tempMinF, decimals) : null,
|
|
2237
|
+
tmax_f: d.tempMaxF !== null ? roundHalfUp2(d.tempMaxF, decimals) : null,
|
|
2238
|
+
tmean_f: d.tempMeanC !== null ? roundHalfUp2(cToF2(d.tempMeanC), decimals) : null,
|
|
2239
|
+
precip_in: precipIn,
|
|
2240
|
+
low_coverage: false,
|
|
2241
|
+
n_obs: d.nObs
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
async function dailyExtremes(station, fromDate, toDate, opts = {}) {
|
|
2245
|
+
const { tz, isUs } = lookupStation(station);
|
|
2246
|
+
const merge = opts.merge ?? "live_v1";
|
|
2247
|
+
const fetchFrom = addUtcDays(fromDate, -1);
|
|
2248
|
+
const fetchTo = addUtcDays(toDate, 1);
|
|
2249
|
+
const rows = await fetchForMode(station, fetchFrom, fetchTo, merge);
|
|
2250
|
+
const extremes = internationalDailyExtremes(rows, {
|
|
2251
|
+
stationTz: tz,
|
|
2252
|
+
precision: isUs ? 1 : 0
|
|
2253
|
+
});
|
|
2254
|
+
return extremes.filter((d) => d.localDate >= fromDate && d.localDate <= toDate).map((d) => projectRow(station.toUpperCase(), d, isUs));
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// ../weather/src/obs.ts
|
|
2258
|
+
function daysBetween(fromDate, toDate) {
|
|
2259
|
+
const from = Date.UTC(
|
|
2260
|
+
Number.parseInt(fromDate.slice(0, 4), 10),
|
|
2261
|
+
Number.parseInt(fromDate.slice(5, 7), 10) - 1,
|
|
2262
|
+
Number.parseInt(fromDate.slice(8, 10), 10)
|
|
2263
|
+
);
|
|
2264
|
+
const to = Date.UTC(
|
|
2265
|
+
Number.parseInt(toDate.slice(0, 4), 10),
|
|
2266
|
+
Number.parseInt(toDate.slice(5, 7), 10) - 1,
|
|
2267
|
+
Number.parseInt(toDate.slice(8, 10), 10)
|
|
1006
2268
|
);
|
|
2269
|
+
return Math.round((to - from) / (24 * 60 * 60 * 1e3)) + 1;
|
|
2270
|
+
}
|
|
2271
|
+
function resolveAutoStrategy(fromDate, toDate) {
|
|
2272
|
+
return daysBetween(fromDate, toDate) <= 7 ? "exact_window" : "warm_cache";
|
|
2273
|
+
}
|
|
2274
|
+
function inchesToMm(inches) {
|
|
2275
|
+
if (inches === null || inches === void 0) return null;
|
|
2276
|
+
return inches * 25.4;
|
|
2277
|
+
}
|
|
2278
|
+
function mbToInhg(mb) {
|
|
2279
|
+
if (mb === null || mb === void 0) return null;
|
|
2280
|
+
return mb * 0.029529983071445;
|
|
2281
|
+
}
|
|
2282
|
+
function fromObservation(o) {
|
|
2283
|
+
const tempC = o.temp_c ?? null;
|
|
2284
|
+
const dewC = o.dewpoint_c ?? null;
|
|
2285
|
+
return {
|
|
2286
|
+
station: o.station_code,
|
|
2287
|
+
observed_at: o.observed_at,
|
|
2288
|
+
source: o.source,
|
|
2289
|
+
temp_c: tempC,
|
|
2290
|
+
temp_f: tempC !== null ? tempC * (9 / 5) + 32 : null,
|
|
2291
|
+
dewpoint_c: dewC,
|
|
2292
|
+
dewpoint_f: dewC !== null ? dewC * (9 / 5) + 32 : null,
|
|
2293
|
+
wind_speed_kts: o.wind_speed_kt ?? null,
|
|
2294
|
+
wind_direction_deg: o.wind_dir_degrees ?? null,
|
|
2295
|
+
pressure_inhg: mbToInhg(o.sea_level_pressure_mb),
|
|
2296
|
+
precip_mm_1h: inchesToMm(o.precip_1hr_inches),
|
|
2297
|
+
raw_metar: o.raw_metar ?? null
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
async function fetchIemForWindow(station, fromDate, toDate, resolvedStrategy) {
|
|
2301
|
+
const out = [];
|
|
2302
|
+
if (resolvedStrategy === "exact_window") {
|
|
2303
|
+
const chunks = await downloadIemAsos(station, fromDate, toDate, {
|
|
2304
|
+
reportType: 3,
|
|
2305
|
+
politenessMs: 1e3
|
|
2306
|
+
});
|
|
2307
|
+
for (const chunk of chunks) {
|
|
2308
|
+
const rows = parseIemCsv(chunk.csv, { observationTypeOverride: "METAR" });
|
|
2309
|
+
for (const r of rows) {
|
|
2310
|
+
const d = r.observed_at.slice(0, 10);
|
|
2311
|
+
if (d >= fromDate && d <= toDate) out.push(fromObservation(r));
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
return out;
|
|
2315
|
+
}
|
|
2316
|
+
const fromYear = Number.parseInt(fromDate.slice(0, 4), 10);
|
|
2317
|
+
const toYear = Number.parseInt(toDate.slice(0, 4), 10);
|
|
2318
|
+
for (let year = fromYear; year <= toYear; year++) {
|
|
2319
|
+
const chunks = await downloadIemAsos(station, `${year}-01-01`, `${year}-12-31`, {
|
|
2320
|
+
reportType: 3,
|
|
2321
|
+
politenessMs: 1e3
|
|
2322
|
+
});
|
|
2323
|
+
for (const chunk of chunks) {
|
|
2324
|
+
const rows = parseIemCsv(chunk.csv, { observationTypeOverride: "METAR" });
|
|
2325
|
+
for (const r of rows) {
|
|
2326
|
+
const d = r.observed_at.slice(0, 10);
|
|
2327
|
+
if (d >= fromDate && d <= toDate) out.push(fromObservation(r));
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
return out;
|
|
2332
|
+
}
|
|
2333
|
+
async function fetchAwcForWindow(station, fromDate, toDate) {
|
|
2334
|
+
const raw = await fetchAwcMetars([station]);
|
|
2335
|
+
const out = [];
|
|
2336
|
+
for (const r of raw) {
|
|
2337
|
+
const obs2 = awcToObservation(r);
|
|
2338
|
+
if (obs2 === null) continue;
|
|
2339
|
+
const d = obs2.observed_at.slice(0, 10);
|
|
2340
|
+
if (d >= fromDate && d <= toDate) out.push(fromObservation(obs2));
|
|
2341
|
+
}
|
|
2342
|
+
return out;
|
|
2343
|
+
}
|
|
2344
|
+
async function fetchByStrategy(station, fromDate, toDate, resolvedStrategy, source) {
|
|
2345
|
+
const wantsIem = source === null || source === void 0 || source === "iem";
|
|
2346
|
+
const wantsAwc = source === null || source === void 0 || source === "awc";
|
|
2347
|
+
const tasks = [];
|
|
2348
|
+
if (wantsIem) tasks.push(fetchIemForWindow(station, fromDate, toDate, resolvedStrategy));
|
|
2349
|
+
if (wantsAwc) tasks.push(fetchAwcForWindow(station, fromDate, toDate).catch(() => []));
|
|
2350
|
+
const results = await Promise.all(tasks);
|
|
2351
|
+
return results.flat();
|
|
2352
|
+
}
|
|
2353
|
+
async function obs(station, fromDate, toDate, opts = {}) {
|
|
2354
|
+
const strategy = opts.strategy ?? "auto";
|
|
2355
|
+
const source = opts.source ?? null;
|
|
2356
|
+
if (source === "ghcnh") {
|
|
2357
|
+
throw new DataAvailabilityError({
|
|
2358
|
+
reason: "model_unavailable",
|
|
2359
|
+
source: "obs.ghcnh",
|
|
2360
|
+
hint: "source='ghcnh' is a valid Python `obs()` filter but the GHCNh fetcher path is not yet wired in the TypeScript SDK. Use source='iem' or source='awc' (or omit `source` for merged) until the TS GHCNh fetcher ships."
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
if (strategy === "hosted") {
|
|
2364
|
+
throw new DataAvailabilityError({
|
|
2365
|
+
reason: "model_unavailable",
|
|
2366
|
+
source: "obs-hosted-stub",
|
|
2367
|
+
hint: "hosted ingest API ships in v0.2.x \u2014 use strategy='exact_window' or 'warm_cache' for v1.x. See https://mostlyright.md/docs/sdk/typescript/ingest-strategies"
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
let resolved;
|
|
2371
|
+
if (strategy === "auto") {
|
|
2372
|
+
resolved = resolveAutoStrategy(fromDate, toDate);
|
|
2373
|
+
} else if (strategy === "exact_window" || strategy === "warm_cache") {
|
|
2374
|
+
resolved = strategy;
|
|
2375
|
+
} else {
|
|
2376
|
+
throw new TypeError(
|
|
2377
|
+
`obs: unknown strategy "${String(strategy)}" \u2014 expected one of: auto, exact_window, warm_cache, hosted`
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
return fetchByStrategy(station, fromDate, toDate, resolved, source);
|
|
1007
2381
|
}
|
|
1008
2382
|
|
|
1009
2383
|
// ../weather/src/index.ts
|
|
@@ -1692,10 +3066,11 @@ replaceTraps((oldTraps) => ({
|
|
|
1692
3066
|
}
|
|
1693
3067
|
}));
|
|
1694
3068
|
|
|
1695
|
-
// ../core/dist/internal/chunk-
|
|
3069
|
+
// ../core/dist/internal/chunk-IPC4XUYW.mjs
|
|
1696
3070
|
function lockKeyFor(key) {
|
|
1697
3071
|
return `mostlyright:cache:lock:${key}`;
|
|
1698
3072
|
}
|
|
3073
|
+
var CACHE_SCHEMA_VERSION = "v2-phase18-integer-f";
|
|
1699
3074
|
var MemoryStore = class {
|
|
1700
3075
|
#entries = /* @__PURE__ */ new Map();
|
|
1701
3076
|
#chain = /* @__PURE__ */ new Map();
|
|
@@ -1842,7 +3217,71 @@ var IndexedDBStore = class {
|
|
|
1842
3217
|
return next;
|
|
1843
3218
|
}
|
|
1844
3219
|
};
|
|
1845
|
-
var
|
|
3220
|
+
var VERSION_FIELD = "_cache_schema_version";
|
|
3221
|
+
function isVersionedEntry(v) {
|
|
3222
|
+
if (v === null || typeof v !== "object") return false;
|
|
3223
|
+
if (!(VERSION_FIELD in v)) return false;
|
|
3224
|
+
return typeof v[VERSION_FIELD] === "string";
|
|
3225
|
+
}
|
|
3226
|
+
function hasListKeys(s) {
|
|
3227
|
+
return typeof s.listKeys === "function";
|
|
3228
|
+
}
|
|
3229
|
+
var VersionedCacheStore = class {
|
|
3230
|
+
#inner;
|
|
3231
|
+
#version;
|
|
3232
|
+
constructor(inner, version4) {
|
|
3233
|
+
if (typeof version4 !== "string" || version4.length === 0) {
|
|
3234
|
+
throw new TypeError("versionedCacheStore: version must be a non-empty string");
|
|
3235
|
+
}
|
|
3236
|
+
this.#inner = inner;
|
|
3237
|
+
this.#version = version4;
|
|
3238
|
+
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Test/diagnostics seam: return the underlying store so tests can assert
|
|
3241
|
+
* which concrete backend `defaultCacheStore()` selected. NOT a production
|
|
3242
|
+
* API — production code MUST use the wrapped store so version
|
|
3243
|
+
* invalidation fires on stale reads.
|
|
3244
|
+
*
|
|
3245
|
+
* @internal
|
|
3246
|
+
*/
|
|
3247
|
+
__peekInner() {
|
|
3248
|
+
return this.#inner;
|
|
3249
|
+
}
|
|
3250
|
+
async get(key) {
|
|
3251
|
+
const raw = await this.#inner.get(key);
|
|
3252
|
+
if (raw === null) return null;
|
|
3253
|
+
if (!isVersionedEntry(raw)) {
|
|
3254
|
+
return null;
|
|
3255
|
+
}
|
|
3256
|
+
if (raw._cache_schema_version !== this.#version) {
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3259
|
+
return raw.value;
|
|
3260
|
+
}
|
|
3261
|
+
async set(key, value, opts) {
|
|
3262
|
+
const wrapped = {
|
|
3263
|
+
value,
|
|
3264
|
+
[VERSION_FIELD]: this.#version
|
|
3265
|
+
};
|
|
3266
|
+
await this.#inner.set(key, wrapped, opts);
|
|
3267
|
+
}
|
|
3268
|
+
async delete(key) {
|
|
3269
|
+
await this.#inner.delete(key);
|
|
3270
|
+
}
|
|
3271
|
+
async withLock(key, fn) {
|
|
3272
|
+
return this.#inner.withLock(key, fn);
|
|
3273
|
+
}
|
|
3274
|
+
async listKeys(prefix) {
|
|
3275
|
+
if (hasListKeys(this.#inner)) {
|
|
3276
|
+
return this.#inner.listKeys(prefix);
|
|
3277
|
+
}
|
|
3278
|
+
return Object.freeze([]);
|
|
3279
|
+
}
|
|
3280
|
+
};
|
|
3281
|
+
function versionedCacheStore(inner, version4) {
|
|
3282
|
+
return new VersionedCacheStore(inner, version4);
|
|
3283
|
+
}
|
|
3284
|
+
var STATIONS3 = [
|
|
1846
3285
|
{
|
|
1847
3286
|
code: "EDDB",
|
|
1848
3287
|
country: "DE",
|
|
@@ -2454,131 +3893,131 @@ var STATIONS = [
|
|
|
2454
3893
|
tz: "Asia/Shanghai"
|
|
2455
3894
|
}
|
|
2456
3895
|
];
|
|
2457
|
-
var
|
|
2458
|
-
["ATL",
|
|
2459
|
-
["AUS",
|
|
2460
|
-
["BOS",
|
|
2461
|
-
["DCA",
|
|
2462
|
-
["DEN",
|
|
2463
|
-
["DFW",
|
|
2464
|
-
["EDDB",
|
|
2465
|
-
["EDDF",
|
|
2466
|
-
["EDDM",
|
|
2467
|
-
["EFHK",
|
|
2468
|
-
["EGKK",
|
|
2469
|
-
["EGLL",
|
|
2470
|
-
["EHAM",
|
|
2471
|
-
["EKCH",
|
|
2472
|
-
["EPWA",
|
|
2473
|
-
["ESSA",
|
|
2474
|
-
["HOU",
|
|
2475
|
-
["LAS",
|
|
2476
|
-
["LAX",
|
|
2477
|
-
["LEBL",
|
|
2478
|
-
["LEMD",
|
|
2479
|
-
["LFPB",
|
|
2480
|
-
["LFPG",
|
|
2481
|
-
["LFPO",
|
|
2482
|
-
["LIMC",
|
|
2483
|
-
["LIRF",
|
|
2484
|
-
["LOWW",
|
|
2485
|
-
["LSZH",
|
|
2486
|
-
["MDW",
|
|
2487
|
-
["MIA",
|
|
2488
|
-
["MSP",
|
|
2489
|
-
["MSY",
|
|
2490
|
-
["NYC",
|
|
2491
|
-
["NZAA",
|
|
2492
|
-
["NZWN",
|
|
2493
|
-
["OERK",
|
|
2494
|
-
["OKC",
|
|
2495
|
-
["OMDB",
|
|
2496
|
-
["OTHH",
|
|
2497
|
-
["PHL",
|
|
2498
|
-
["PHX",
|
|
2499
|
-
["RCTP",
|
|
2500
|
-
["RJAA",
|
|
2501
|
-
["RJTT",
|
|
2502
|
-
["RKSI",
|
|
2503
|
-
["SAEZ",
|
|
2504
|
-
["SAT",
|
|
2505
|
-
["SBGR",
|
|
2506
|
-
["SEA",
|
|
2507
|
-
["SFO",
|
|
2508
|
-
["UUEE",
|
|
2509
|
-
["VABB",
|
|
2510
|
-
["VHHH",
|
|
2511
|
-
["VIDP",
|
|
2512
|
-
["VTBS",
|
|
2513
|
-
["WSSS",
|
|
2514
|
-
["YBBN",
|
|
2515
|
-
["YMML",
|
|
2516
|
-
["YSSY",
|
|
2517
|
-
["ZBAA",
|
|
2518
|
-
["ZSPD",
|
|
3896
|
+
var STATION_BY_CODE3 = /* @__PURE__ */ new Map([
|
|
3897
|
+
["ATL", STATIONS3[10]],
|
|
3898
|
+
["AUS", STATIONS3[11]],
|
|
3899
|
+
["BOS", STATIONS3[12]],
|
|
3900
|
+
["DCA", STATIONS3[13]],
|
|
3901
|
+
["DEN", STATIONS3[14]],
|
|
3902
|
+
["DFW", STATIONS3[15]],
|
|
3903
|
+
["EDDB", STATIONS3[0]],
|
|
3904
|
+
["EDDF", STATIONS3[1]],
|
|
3905
|
+
["EDDM", STATIONS3[2]],
|
|
3906
|
+
["EFHK", STATIONS3[3]],
|
|
3907
|
+
["EGKK", STATIONS3[4]],
|
|
3908
|
+
["EGLL", STATIONS3[5]],
|
|
3909
|
+
["EHAM", STATIONS3[6]],
|
|
3910
|
+
["EKCH", STATIONS3[7]],
|
|
3911
|
+
["EPWA", STATIONS3[8]],
|
|
3912
|
+
["ESSA", STATIONS3[9]],
|
|
3913
|
+
["HOU", STATIONS3[16]],
|
|
3914
|
+
["LAS", STATIONS3[17]],
|
|
3915
|
+
["LAX", STATIONS3[18]],
|
|
3916
|
+
["LEBL", STATIONS3[30]],
|
|
3917
|
+
["LEMD", STATIONS3[31]],
|
|
3918
|
+
["LFPB", STATIONS3[32]],
|
|
3919
|
+
["LFPG", STATIONS3[33]],
|
|
3920
|
+
["LFPO", STATIONS3[34]],
|
|
3921
|
+
["LIMC", STATIONS3[35]],
|
|
3922
|
+
["LIRF", STATIONS3[36]],
|
|
3923
|
+
["LOWW", STATIONS3[37]],
|
|
3924
|
+
["LSZH", STATIONS3[38]],
|
|
3925
|
+
["MDW", STATIONS3[19]],
|
|
3926
|
+
["MIA", STATIONS3[20]],
|
|
3927
|
+
["MSP", STATIONS3[21]],
|
|
3928
|
+
["MSY", STATIONS3[22]],
|
|
3929
|
+
["NYC", STATIONS3[23]],
|
|
3930
|
+
["NZAA", STATIONS3[39]],
|
|
3931
|
+
["NZWN", STATIONS3[40]],
|
|
3932
|
+
["OERK", STATIONS3[41]],
|
|
3933
|
+
["OKC", STATIONS3[24]],
|
|
3934
|
+
["OMDB", STATIONS3[42]],
|
|
3935
|
+
["OTHH", STATIONS3[43]],
|
|
3936
|
+
["PHL", STATIONS3[25]],
|
|
3937
|
+
["PHX", STATIONS3[26]],
|
|
3938
|
+
["RCTP", STATIONS3[44]],
|
|
3939
|
+
["RJAA", STATIONS3[45]],
|
|
3940
|
+
["RJTT", STATIONS3[46]],
|
|
3941
|
+
["RKSI", STATIONS3[47]],
|
|
3942
|
+
["SAEZ", STATIONS3[48]],
|
|
3943
|
+
["SAT", STATIONS3[27]],
|
|
3944
|
+
["SBGR", STATIONS3[49]],
|
|
3945
|
+
["SEA", STATIONS3[28]],
|
|
3946
|
+
["SFO", STATIONS3[29]],
|
|
3947
|
+
["UUEE", STATIONS3[50]],
|
|
3948
|
+
["VABB", STATIONS3[51]],
|
|
3949
|
+
["VHHH", STATIONS3[52]],
|
|
3950
|
+
["VIDP", STATIONS3[53]],
|
|
3951
|
+
["VTBS", STATIONS3[54]],
|
|
3952
|
+
["WSSS", STATIONS3[55]],
|
|
3953
|
+
["YBBN", STATIONS3[56]],
|
|
3954
|
+
["YMML", STATIONS3[57]],
|
|
3955
|
+
["YSSY", STATIONS3[58]],
|
|
3956
|
+
["ZBAA", STATIONS3[59]],
|
|
3957
|
+
["ZSPD", STATIONS3[60]]
|
|
2519
3958
|
]);
|
|
2520
|
-
var
|
|
2521
|
-
["EDDB",
|
|
2522
|
-
["EDDF",
|
|
2523
|
-
["EDDM",
|
|
2524
|
-
["EFHK",
|
|
2525
|
-
["EGKK",
|
|
2526
|
-
["EGLL",
|
|
2527
|
-
["EHAM",
|
|
2528
|
-
["EKCH",
|
|
2529
|
-
["EPWA",
|
|
2530
|
-
["ESSA",
|
|
2531
|
-
["KATL",
|
|
2532
|
-
["KAUS",
|
|
2533
|
-
["KBOS",
|
|
2534
|
-
["KDCA",
|
|
2535
|
-
["KDEN",
|
|
2536
|
-
["KDFW",
|
|
2537
|
-
["KHOU",
|
|
2538
|
-
["KLAS",
|
|
2539
|
-
["KLAX",
|
|
2540
|
-
["KMDW",
|
|
2541
|
-
["KMIA",
|
|
2542
|
-
["KMSP",
|
|
2543
|
-
["KMSY",
|
|
2544
|
-
["KNYC",
|
|
2545
|
-
["KOKC",
|
|
2546
|
-
["KPHL",
|
|
2547
|
-
["KPHX",
|
|
2548
|
-
["KSAT",
|
|
2549
|
-
["KSEA",
|
|
2550
|
-
["KSFO",
|
|
2551
|
-
["LEBL",
|
|
2552
|
-
["LEMD",
|
|
2553
|
-
["LFPB",
|
|
2554
|
-
["LFPG",
|
|
2555
|
-
["LFPO",
|
|
2556
|
-
["LIMC",
|
|
2557
|
-
["LIRF",
|
|
2558
|
-
["LOWW",
|
|
2559
|
-
["LSZH",
|
|
2560
|
-
["NZAA",
|
|
2561
|
-
["NZWN",
|
|
2562
|
-
["OERK",
|
|
2563
|
-
["OMDB",
|
|
2564
|
-
["OTHH",
|
|
2565
|
-
["RCTP",
|
|
2566
|
-
["RJAA",
|
|
2567
|
-
["RJTT",
|
|
2568
|
-
["RKSI",
|
|
2569
|
-
["SAEZ",
|
|
2570
|
-
["SBGR",
|
|
2571
|
-
["UUEE",
|
|
2572
|
-
["VABB",
|
|
2573
|
-
["VHHH",
|
|
2574
|
-
["VIDP",
|
|
2575
|
-
["VTBS",
|
|
2576
|
-
["WSSS",
|
|
2577
|
-
["YBBN",
|
|
2578
|
-
["YMML",
|
|
2579
|
-
["YSSY",
|
|
2580
|
-
["ZBAA",
|
|
2581
|
-
["ZSPD",
|
|
3959
|
+
var STATION_BY_ICAO3 = /* @__PURE__ */ new Map([
|
|
3960
|
+
["EDDB", STATIONS3[0]],
|
|
3961
|
+
["EDDF", STATIONS3[1]],
|
|
3962
|
+
["EDDM", STATIONS3[2]],
|
|
3963
|
+
["EFHK", STATIONS3[3]],
|
|
3964
|
+
["EGKK", STATIONS3[4]],
|
|
3965
|
+
["EGLL", STATIONS3[5]],
|
|
3966
|
+
["EHAM", STATIONS3[6]],
|
|
3967
|
+
["EKCH", STATIONS3[7]],
|
|
3968
|
+
["EPWA", STATIONS3[8]],
|
|
3969
|
+
["ESSA", STATIONS3[9]],
|
|
3970
|
+
["KATL", STATIONS3[10]],
|
|
3971
|
+
["KAUS", STATIONS3[11]],
|
|
3972
|
+
["KBOS", STATIONS3[12]],
|
|
3973
|
+
["KDCA", STATIONS3[13]],
|
|
3974
|
+
["KDEN", STATIONS3[14]],
|
|
3975
|
+
["KDFW", STATIONS3[15]],
|
|
3976
|
+
["KHOU", STATIONS3[16]],
|
|
3977
|
+
["KLAS", STATIONS3[17]],
|
|
3978
|
+
["KLAX", STATIONS3[18]],
|
|
3979
|
+
["KMDW", STATIONS3[19]],
|
|
3980
|
+
["KMIA", STATIONS3[20]],
|
|
3981
|
+
["KMSP", STATIONS3[21]],
|
|
3982
|
+
["KMSY", STATIONS3[22]],
|
|
3983
|
+
["KNYC", STATIONS3[23]],
|
|
3984
|
+
["KOKC", STATIONS3[24]],
|
|
3985
|
+
["KPHL", STATIONS3[25]],
|
|
3986
|
+
["KPHX", STATIONS3[26]],
|
|
3987
|
+
["KSAT", STATIONS3[27]],
|
|
3988
|
+
["KSEA", STATIONS3[28]],
|
|
3989
|
+
["KSFO", STATIONS3[29]],
|
|
3990
|
+
["LEBL", STATIONS3[30]],
|
|
3991
|
+
["LEMD", STATIONS3[31]],
|
|
3992
|
+
["LFPB", STATIONS3[32]],
|
|
3993
|
+
["LFPG", STATIONS3[33]],
|
|
3994
|
+
["LFPO", STATIONS3[34]],
|
|
3995
|
+
["LIMC", STATIONS3[35]],
|
|
3996
|
+
["LIRF", STATIONS3[36]],
|
|
3997
|
+
["LOWW", STATIONS3[37]],
|
|
3998
|
+
["LSZH", STATIONS3[38]],
|
|
3999
|
+
["NZAA", STATIONS3[39]],
|
|
4000
|
+
["NZWN", STATIONS3[40]],
|
|
4001
|
+
["OERK", STATIONS3[41]],
|
|
4002
|
+
["OMDB", STATIONS3[42]],
|
|
4003
|
+
["OTHH", STATIONS3[43]],
|
|
4004
|
+
["RCTP", STATIONS3[44]],
|
|
4005
|
+
["RJAA", STATIONS3[45]],
|
|
4006
|
+
["RJTT", STATIONS3[46]],
|
|
4007
|
+
["RKSI", STATIONS3[47]],
|
|
4008
|
+
["SAEZ", STATIONS3[48]],
|
|
4009
|
+
["SBGR", STATIONS3[49]],
|
|
4010
|
+
["UUEE", STATIONS3[50]],
|
|
4011
|
+
["VABB", STATIONS3[51]],
|
|
4012
|
+
["VHHH", STATIONS3[52]],
|
|
4013
|
+
["VIDP", STATIONS3[53]],
|
|
4014
|
+
["VTBS", STATIONS3[54]],
|
|
4015
|
+
["WSSS", STATIONS3[55]],
|
|
4016
|
+
["YBBN", STATIONS3[56]],
|
|
4017
|
+
["YMML", STATIONS3[57]],
|
|
4018
|
+
["YSSY", STATIONS3[58]],
|
|
4019
|
+
["ZBAA", STATIONS3[59]],
|
|
4020
|
+
["ZSPD", STATIONS3[60]]
|
|
2582
4021
|
]);
|
|
2583
4022
|
var _STATION_TZ = Object.freeze({
|
|
2584
4023
|
// Eastern (UTC-5 standard / UTC-4 DST)
|
|
@@ -2725,13 +4164,13 @@ function _lstOffsetHours(stationTz) {
|
|
|
2725
4164
|
}
|
|
2726
4165
|
function _lstOffsetHoursFor(station) {
|
|
2727
4166
|
const upper = station.trim().toUpperCase();
|
|
2728
|
-
const byCode =
|
|
4167
|
+
const byCode = STATION_BY_CODE3.get(upper);
|
|
2729
4168
|
if (byCode !== void 0) return _lstOffsetHours(byCode.tz);
|
|
2730
|
-
const byIcao =
|
|
4169
|
+
const byIcao = STATION_BY_ICAO3.get(upper);
|
|
2731
4170
|
if (byIcao !== void 0) return _lstOffsetHours(byIcao.tz);
|
|
2732
4171
|
if (upper.length === 4 && upper.startsWith("K")) {
|
|
2733
4172
|
const stripped = upper.slice(1);
|
|
2734
|
-
const retry =
|
|
4173
|
+
const retry = STATION_BY_CODE3.get(stripped);
|
|
2735
4174
|
if (retry !== void 0) return _lstOffsetHours(retry.tz);
|
|
2736
4175
|
}
|
|
2737
4176
|
throw new RangeError(`unknown station: ${JSON.stringify(station)}`);
|
|
@@ -2803,8 +4242,8 @@ function cacheKeyForClimate(station, year) {
|
|
|
2803
4242
|
|
|
2804
4243
|
// ../core/dist/internal/cache/index.browser.mjs
|
|
2805
4244
|
async function defaultCacheStore() {
|
|
2806
|
-
|
|
2807
|
-
return
|
|
4245
|
+
const inner = typeof indexedDB !== "undefined" ? new IndexedDBStore() : new MemoryStore();
|
|
4246
|
+
return versionedCacheStore(inner, CACHE_SCHEMA_VERSION);
|
|
2808
4247
|
}
|
|
2809
4248
|
|
|
2810
4249
|
// ../core/dist/internal/pairs.mjs
|
|
@@ -2990,9 +4429,9 @@ function marketCloseUtc(dateStr, station, tzOverride) {
|
|
|
2990
4429
|
);
|
|
2991
4430
|
return new Date(marketCloseAsUtcMs - offsetHours * 36e5);
|
|
2992
4431
|
}
|
|
2993
|
-
function collectNonNull(
|
|
4432
|
+
function collectNonNull(obs2, key) {
|
|
2994
4433
|
const out = [];
|
|
2995
|
-
for (const o of
|
|
4434
|
+
for (const o of obs2) {
|
|
2996
4435
|
const v = o[key];
|
|
2997
4436
|
if (typeof v === "number" && Number.isFinite(v)) out.push(v);
|
|
2998
4437
|
}
|
|
@@ -3087,13 +4526,68 @@ function buildPairsRow(dateStr, station, observations, climate, opts = {}) {
|
|
|
3087
4526
|
function buildPairs(station, dates, observationsByDate, climateByDate, opts = {}) {
|
|
3088
4527
|
const out = [];
|
|
3089
4528
|
for (const date of dates) {
|
|
3090
|
-
const
|
|
4529
|
+
const obs2 = observationsByDate[date] ?? [];
|
|
3091
4530
|
const climate = climateByDate[date] ?? null;
|
|
3092
|
-
out.push(buildPairsRow(date, station,
|
|
4531
|
+
out.push(buildPairsRow(date, station, obs2, climate, opts));
|
|
3093
4532
|
}
|
|
3094
4533
|
return Object.freeze(out);
|
|
3095
4534
|
}
|
|
3096
4535
|
|
|
4536
|
+
// src/research.types.ts
|
|
4537
|
+
var KNOWN_RESEARCH_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
4538
|
+
// Pre-Phase-21 fetcher controls.
|
|
4539
|
+
"signal",
|
|
4540
|
+
"awcHours",
|
|
4541
|
+
"iemPolitenessMs",
|
|
4542
|
+
"ghcnhPolitenessMs",
|
|
4543
|
+
"cliPolitenessMs",
|
|
4544
|
+
"now",
|
|
4545
|
+
"cache",
|
|
4546
|
+
// Pre-Phase-21 selectors.
|
|
4547
|
+
"city",
|
|
4548
|
+
"contract",
|
|
4549
|
+
"contracts",
|
|
4550
|
+
"stationOverride",
|
|
4551
|
+
"sources",
|
|
4552
|
+
"source",
|
|
4553
|
+
"includeTrades",
|
|
4554
|
+
"onWarning",
|
|
4555
|
+
// Phase 21 21-01: Python-parity composable kwargs.
|
|
4556
|
+
"include_forecast",
|
|
4557
|
+
"forecast_model",
|
|
4558
|
+
"forecast_models",
|
|
4559
|
+
"qc",
|
|
4560
|
+
"tz_override",
|
|
4561
|
+
"backend",
|
|
4562
|
+
"return_type"
|
|
4563
|
+
]);
|
|
4564
|
+
function validateResearchKwargs(opts) {
|
|
4565
|
+
const present = (v) => v !== void 0 && v !== null;
|
|
4566
|
+
for (const key of Object.keys(opts)) {
|
|
4567
|
+
if (!KNOWN_RESEARCH_OPTION_KEYS.has(key)) {
|
|
4568
|
+
throw new TypeError(
|
|
4569
|
+
`research(): unknown option key ${JSON.stringify(key)}. Valid keys: ${[...KNOWN_RESEARCH_OPTION_KEYS].sort().join(", ")}`
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
if (present(opts.sources) && present(opts.source)) {
|
|
4574
|
+
throw new TypeError(
|
|
4575
|
+
"research(): sources= and source= are mutually exclusive \u2014 use `sources=` for the LIVE_V1 multi-source selector or `source=` for a single-source query, not both"
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
if (present(opts.forecast_model) && present(opts.forecast_models)) {
|
|
4579
|
+
throw new TypeError(
|
|
4580
|
+
"research(): forecast_model= and forecast_models= are mutually exclusive \u2014 use `forecast_models=` for multi-model fan-out or `forecast_model=` for a single model, not both"
|
|
4581
|
+
);
|
|
4582
|
+
}
|
|
4583
|
+
const wantsForecast = present(opts.forecast_model) || present(opts.forecast_models);
|
|
4584
|
+
if (wantsForecast && opts.include_forecast !== true) {
|
|
4585
|
+
throw new TypeError(
|
|
4586
|
+
"research(): forecast_model=/forecast_models= require include_forecast=true; the model filter is otherwise silently ignored"
|
|
4587
|
+
);
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
|
|
3097
4591
|
// src/research.ts
|
|
3098
4592
|
var AWC_MAX_HOURS2 = 168;
|
|
3099
4593
|
async function resolveCache(opts) {
|
|
@@ -3361,9 +4855,9 @@ async function fetchIemAsosWithCache(stationCode, _fromYear, _extendedToYear, fr
|
|
|
3361
4855
|
}
|
|
3362
4856
|
}
|
|
3363
4857
|
}
|
|
3364
|
-
for (const
|
|
3365
|
-
const obsDate =
|
|
3366
|
-
if (obsDate >= fromDate && obsDate <= extendedTo) acc.push(
|
|
4858
|
+
for (const obs2 of monthRows) {
|
|
4859
|
+
const obsDate = obs2.observed_at.slice(0, 10);
|
|
4860
|
+
if (obsDate >= fromDate && obsDate <= extendedTo) acc.push(obs2);
|
|
3367
4861
|
}
|
|
3368
4862
|
}
|
|
3369
4863
|
return acc;
|
|
@@ -3434,14 +4928,22 @@ async function fetchGhcnhWithCache(stationCode, ghcnhId, fromDate, extendedTo, o
|
|
|
3434
4928
|
}
|
|
3435
4929
|
}
|
|
3436
4930
|
}
|
|
3437
|
-
for (const
|
|
3438
|
-
const obsDate =
|
|
3439
|
-
if (obsDate >= fromDate && obsDate <= extendedTo) acc.push(
|
|
4931
|
+
for (const obs2 of monthRows) {
|
|
4932
|
+
const obsDate = obs2.observed_at.slice(0, 10);
|
|
4933
|
+
if (obsDate >= fromDate && obsDate <= extendedTo) acc.push(obs2);
|
|
3440
4934
|
}
|
|
3441
4935
|
}
|
|
3442
4936
|
return acc;
|
|
3443
4937
|
}
|
|
3444
4938
|
async function research(station, fromDate, toDate, opts = {}) {
|
|
4939
|
+
validateResearchKwargs(opts);
|
|
4940
|
+
if (opts.backend === "polars") {
|
|
4941
|
+
throw new DataAvailabilityError({
|
|
4942
|
+
reason: "model_unavailable",
|
|
4943
|
+
hint: 'polars backend not available in TypeScript SDK; use Python (mostlyrightmd) for backend="polars". TS returns plain object arrays.',
|
|
4944
|
+
source: "research.backend"
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
3445
4947
|
const hasCity = typeof opts.city === "string" && opts.city.length > 0;
|
|
3446
4948
|
const hasContract = typeof opts.contract === "string" && opts.contract.length > 0;
|
|
3447
4949
|
const hasContracts = Array.isArray(opts.contracts) && opts.contracts.length > 0;
|
|
@@ -3517,8 +5019,8 @@ async function research(station, fromDate, toDate, opts = {}) {
|
|
|
3517
5019
|
if (opts.signal !== void 0) awcOpts.signal = opts.signal;
|
|
3518
5020
|
const awcRaw = await fetchAwcMetars([resolved.icao], awcOpts);
|
|
3519
5021
|
for (const m of awcRaw) {
|
|
3520
|
-
const
|
|
3521
|
-
if (
|
|
5022
|
+
const obs2 = awcToObservation(m);
|
|
5023
|
+
if (obs2 !== null) awcRows.push(obs2);
|
|
3522
5024
|
}
|
|
3523
5025
|
}
|
|
3524
5026
|
const iemRows = await fetchIemAsosWithCache(
|
|
@@ -3549,8 +5051,8 @@ async function research(station, fromDate, toDate, opts = {}) {
|
|
|
3549
5051
|
const observationsByDate = {};
|
|
3550
5052
|
const dateLo = dates[0] ?? "";
|
|
3551
5053
|
const dateHi = dates[dates.length - 1] ?? "";
|
|
3552
|
-
for (const
|
|
3553
|
-
const settleDate = observedSettlementDate(
|
|
5054
|
+
for (const obs2 of merged) {
|
|
5055
|
+
const settleDate = observedSettlementDate(obs2.observed_at, resolved.code);
|
|
3554
5056
|
if (settleDate === null) continue;
|
|
3555
5057
|
if (settleDate < dateLo || settleDate > dateHi) continue;
|
|
3556
5058
|
let bucket = observationsByDate[settleDate];
|
|
@@ -3558,7 +5060,7 @@ async function research(station, fromDate, toDate, opts = {}) {
|
|
|
3558
5060
|
bucket = [];
|
|
3559
5061
|
observationsByDate[settleDate] = bucket;
|
|
3560
5062
|
}
|
|
3561
|
-
bucket.push(
|
|
5063
|
+
bucket.push(obs2);
|
|
3562
5064
|
}
|
|
3563
5065
|
const climateByDate = {};
|
|
3564
5066
|
for (const cli of mergedClimate) {
|
|
@@ -3692,8 +5194,8 @@ async function researchBySource(station, source, fromDate, toDate, opts = {}) {
|
|
|
3692
5194
|
const raw = await fetchAwcMetars([resolved.icao], awcOpts);
|
|
3693
5195
|
const parsed = [];
|
|
3694
5196
|
for (const m of raw) {
|
|
3695
|
-
const
|
|
3696
|
-
if (
|
|
5197
|
+
const obs2 = awcToObservation(m);
|
|
5198
|
+
if (obs2 !== null) parsed.push(obs2);
|
|
3697
5199
|
}
|
|
3698
5200
|
rows = parsed.filter((r) => {
|
|
3699
5201
|
const d = r.observed_at.slice(0, 10);
|
|
@@ -3983,13 +5485,135 @@ function discover(args) {
|
|
|
3983
5485
|
});
|
|
3984
5486
|
}
|
|
3985
5487
|
|
|
5488
|
+
// ../core/dist/preprocessing/index.mjs
|
|
5489
|
+
var preprocessing_exports = {};
|
|
5490
|
+
__export(preprocessing_exports, {
|
|
5491
|
+
PHYSICS_BOUNDS: () => PHYSICS_BOUNDS,
|
|
5492
|
+
clipOutliers: () => clipOutliers,
|
|
5493
|
+
iemCrosscheck: () => crosscheckIemGhcnh
|
|
5494
|
+
});
|
|
5495
|
+
var PHYSICS_BOUNDS = /* @__PURE__ */ new Map([
|
|
5496
|
+
["temp_c", [-89, 57]],
|
|
5497
|
+
["dew_point_c", [-89, 35]],
|
|
5498
|
+
["dewpoint_c", [-89, 35]],
|
|
5499
|
+
["wind_speed_ms", [0, 100]],
|
|
5500
|
+
["wind_speed_kt", [0, 200]],
|
|
5501
|
+
["wind_dir_deg", [0, 360]],
|
|
5502
|
+
["wind_dir_degrees", [0, 360]],
|
|
5503
|
+
["slp_hpa", [870, 1085]],
|
|
5504
|
+
["sea_level_pressure_mb", [870, 1085]],
|
|
5505
|
+
["relative_humidity_pct_2m", [0, 100]],
|
|
5506
|
+
["precip_mm_1h", [0, 305]]
|
|
5507
|
+
]);
|
|
5508
|
+
function clipOutliers(rows, col, opts = {}) {
|
|
5509
|
+
const std = opts.std ?? 3;
|
|
5510
|
+
const key = `${col}_clipped`;
|
|
5511
|
+
let lo;
|
|
5512
|
+
let hi;
|
|
5513
|
+
let passThrough = false;
|
|
5514
|
+
if (opts.bounds !== void 0) {
|
|
5515
|
+
[lo, hi] = opts.bounds;
|
|
5516
|
+
} else if (PHYSICS_BOUNDS.has(col)) {
|
|
5517
|
+
const b = PHYSICS_BOUNDS.get(col);
|
|
5518
|
+
if (b === void 0) {
|
|
5519
|
+
throw new Error(`PHYSICS_BOUNDS.get(${col}) unexpectedly undefined`);
|
|
5520
|
+
}
|
|
5521
|
+
[lo, hi] = b;
|
|
5522
|
+
} else {
|
|
5523
|
+
if (!Number.isFinite(std) || std <= 0) {
|
|
5524
|
+
throw new RangeError(
|
|
5525
|
+
`clipOutliers: std must be > 0 for the sigma fallback (got ${std}); pass bounds=[lo, hi] or use a physics-default column`
|
|
5526
|
+
);
|
|
5527
|
+
}
|
|
5528
|
+
const vals = [];
|
|
5529
|
+
for (const r of rows) {
|
|
5530
|
+
const v = r?.[col];
|
|
5531
|
+
if (typeof v === "number" && Number.isFinite(v)) vals.push(v);
|
|
5532
|
+
}
|
|
5533
|
+
if (vals.length < 2) {
|
|
5534
|
+
passThrough = true;
|
|
5535
|
+
lo = Number.NEGATIVE_INFINITY;
|
|
5536
|
+
hi = Number.POSITIVE_INFINITY;
|
|
5537
|
+
} else {
|
|
5538
|
+
const mu = vals.reduce((a, b) => a + b, 0) / vals.length;
|
|
5539
|
+
const sumSq = vals.reduce((a, b) => a + (b - mu) ** 2, 0);
|
|
5540
|
+
const sigma = Math.sqrt(sumSq / (vals.length - 1));
|
|
5541
|
+
if (sigma === 0 || !Number.isFinite(sigma)) {
|
|
5542
|
+
passThrough = true;
|
|
5543
|
+
lo = Number.NEGATIVE_INFINITY;
|
|
5544
|
+
hi = Number.POSITIVE_INFINITY;
|
|
5545
|
+
} else {
|
|
5546
|
+
lo = mu - std * sigma;
|
|
5547
|
+
hi = mu + std * sigma;
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
const out = [];
|
|
5552
|
+
for (const r of rows) {
|
|
5553
|
+
const v = r?.[col];
|
|
5554
|
+
let clipped;
|
|
5555
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
5556
|
+
clipped = passThrough ? v : Math.min(Math.max(v, lo), hi);
|
|
5557
|
+
} else {
|
|
5558
|
+
clipped = null;
|
|
5559
|
+
}
|
|
5560
|
+
out.push({ ...r, [key]: clipped });
|
|
5561
|
+
}
|
|
5562
|
+
return out;
|
|
5563
|
+
}
|
|
5564
|
+
function crosscheckIemGhcnh(iemRows, ghcnhRows, opts = {}) {
|
|
5565
|
+
const tolC = opts.tolC ?? 2;
|
|
5566
|
+
if (iemRows.length === 0 || ghcnhRows.length === 0) return [];
|
|
5567
|
+
for (const r of iemRows) {
|
|
5568
|
+
if (typeof r?.station !== "string" || typeof r?.eventTime !== "string") {
|
|
5569
|
+
throw new Error(
|
|
5570
|
+
"crosscheckIemGhcnh: iem rows must carry 'station' (string) and 'eventTime' (string) keys"
|
|
5571
|
+
);
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
for (const r of ghcnhRows) {
|
|
5575
|
+
if (typeof r?.station !== "string" || typeof r?.eventTime !== "string") {
|
|
5576
|
+
throw new Error(
|
|
5577
|
+
"crosscheckIemGhcnh: ghcnh rows must carry 'station' (string) and 'eventTime' (string) keys"
|
|
5578
|
+
);
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
const iemMap = /* @__PURE__ */ new Map();
|
|
5582
|
+
for (const r of iemRows) {
|
|
5583
|
+
const key = `${r.station}|${r.eventTime}`;
|
|
5584
|
+
iemMap.set(key, r);
|
|
5585
|
+
}
|
|
5586
|
+
const out = [];
|
|
5587
|
+
for (const g of ghcnhRows) {
|
|
5588
|
+
const key = `${g.station}|${g.eventTime}`;
|
|
5589
|
+
const i = iemMap.get(key);
|
|
5590
|
+
if (i === void 0) continue;
|
|
5591
|
+
const iT = typeof i.temp_c === "number" && Number.isFinite(i.temp_c) ? i.temp_c : null;
|
|
5592
|
+
const gT = typeof g.temp_c === "number" && Number.isFinite(g.temp_c) ? g.temp_c : null;
|
|
5593
|
+
if (iT === null || gT === null) continue;
|
|
5594
|
+
const delta = Math.abs(iT - gT);
|
|
5595
|
+
if (delta > tolC) {
|
|
5596
|
+
out.push({
|
|
5597
|
+
station: g.station,
|
|
5598
|
+
eventTime: g.eventTime,
|
|
5599
|
+
tempCIem: iT,
|
|
5600
|
+
tempCGhcnh: gT,
|
|
5601
|
+
deltaC: delta
|
|
5602
|
+
});
|
|
5603
|
+
}
|
|
5604
|
+
}
|
|
5605
|
+
return out;
|
|
5606
|
+
}
|
|
5607
|
+
|
|
3986
5608
|
// src/index.ts
|
|
5609
|
+
var Preprocessing = preprocessing_exports;
|
|
3987
5610
|
var version3 = "0.0.0";
|
|
3988
5611
|
export {
|
|
3989
5612
|
LiveStreamError,
|
|
3990
5613
|
MODE2_SOURCES,
|
|
3991
5614
|
NoLiveDataError,
|
|
3992
5615
|
POLITE_FLOORS_S,
|
|
5616
|
+
Preprocessing,
|
|
3993
5617
|
SELECTOR_NAMES,
|
|
3994
5618
|
SOURCE_ALIASES,
|
|
3995
5619
|
SOURCE_IDENTITY_TAGS,
|
|
@@ -3998,6 +5622,7 @@ export {
|
|
|
3998
5622
|
assertSourceIdentity,
|
|
3999
5623
|
buildOverrideWarning,
|
|
4000
5624
|
src_exports as core,
|
|
5625
|
+
dailyExtremes,
|
|
4001
5626
|
discover,
|
|
4002
5627
|
helloCore,
|
|
4003
5628
|
helloMarkets,
|
|
@@ -4006,6 +5631,8 @@ export {
|
|
|
4006
5631
|
isMode2Source,
|
|
4007
5632
|
latest,
|
|
4008
5633
|
src_exports3 as markets,
|
|
5634
|
+
obs,
|
|
5635
|
+
preprocessing_exports as preprocessing,
|
|
4009
5636
|
research,
|
|
4010
5637
|
researchBySource,
|
|
4011
5638
|
resolveCity,
|