@aguacerowx/mapsgl 0.0.55 → 0.0.57
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/index.js +5 -6
- package/package.json +3 -4
- package/src/WeatherLayerManager.js +3 -5
- package/src/NwsWatchesWarningsOverlay.js +0 -977
- package/src/nwsAlertsFetchSpec.js +0 -114
- package/src/nwsAlertsSupport.js +0 -1337
- package/src/nwsEventColorsDefaults.js +0 -133
- package/src/nwsSdkConstants.js +0 -368
- package/src/nwsWarningCustomizationKey.gen.js +0 -493
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
// ../../../aguacero-frontend/src/components/WarningsMenu/nwsWarningCustomizationKey.ts
|
|
2
|
-
var BASE_TORNADO = "Tornado Warning";
|
|
3
|
-
var BASE_SEVERE = "Severe Thunderstorm Warning";
|
|
4
|
-
var BASE_FLASH_FLOOD = "Flash Flood Warning";
|
|
5
|
-
function nwsAlertTagBlob(properties) {
|
|
6
|
-
if (!properties) return "";
|
|
7
|
-
const parts = [
|
|
8
|
-
properties.description,
|
|
9
|
-
properties.headline,
|
|
10
|
-
properties.summary,
|
|
11
|
-
properties.raw_text,
|
|
12
|
-
properties.raw,
|
|
13
|
-
properties.full_text,
|
|
14
|
-
properties.body,
|
|
15
|
-
properties.message,
|
|
16
|
-
properties.text,
|
|
17
|
-
properties.product_text,
|
|
18
|
-
properties.alert_text,
|
|
19
|
-
properties.instruction,
|
|
20
|
-
properties.detail
|
|
21
|
-
];
|
|
22
|
-
const tagJoin = Array.isArray(properties.tags) && properties.tags.length > 0 ? properties.tags.filter((x) => x != null && String(x).trim() !== "").map((x) => String(x)).join("\n") : "";
|
|
23
|
-
return [...parts, tagJoin].filter((x) => x != null && String(x).trim() !== "").join("\n");
|
|
24
|
-
}
|
|
25
|
-
function normalizeNwsWarningTagText(raw) {
|
|
26
|
-
return raw.replace(/\u2026/g, " ").replace(/\.{2,}/g, " ").replace(/\s+/g, " ").trim().toUpperCase();
|
|
27
|
-
}
|
|
28
|
-
function hasTornadoObservedTag(blob) {
|
|
29
|
-
return /TORNADO[\s.\-–—/:]*OBSERVED/.test(blob);
|
|
30
|
-
}
|
|
31
|
-
function compactPropertyKeyForTornadoField(key) {
|
|
32
|
-
return String(key).toLowerCase().replace(/[\s_-]+/g, "");
|
|
33
|
-
}
|
|
34
|
-
function hasTornadoObservedFromExplicitFeatureProperties(properties) {
|
|
35
|
-
if (!properties) return false;
|
|
36
|
-
const tornadoKeys = /* @__PURE__ */ new Set([
|
|
37
|
-
"tornado",
|
|
38
|
-
"tornadothreat",
|
|
39
|
-
"tornadomode",
|
|
40
|
-
"tornadostatus",
|
|
41
|
-
"tornadotag",
|
|
42
|
-
"tornadotype"
|
|
43
|
-
]);
|
|
44
|
-
for (const [key, val] of Object.entries(properties)) {
|
|
45
|
-
if (!tornadoKeys.has(compactPropertyKeyForTornadoField(key))) continue;
|
|
46
|
-
const vs = String(val).trim().toLowerCase();
|
|
47
|
-
if (vs === "observed") return true;
|
|
48
|
-
if (vs.startsWith("[") || vs.startsWith("{")) {
|
|
49
|
-
try {
|
|
50
|
-
const parsed = JSON.parse(String(val));
|
|
51
|
-
if (parsed === "observed") return true;
|
|
52
|
-
if (Array.isArray(parsed) && parsed.some((x) => String(x).trim().toLowerCase() === "observed")) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
function hasTornadoObservedFromStructuredCap(properties) {
|
|
62
|
-
if (!properties) return false;
|
|
63
|
-
let params = properties.parameters;
|
|
64
|
-
if (typeof params === "string") {
|
|
65
|
-
try {
|
|
66
|
-
params = JSON.parse(params);
|
|
67
|
-
} catch {
|
|
68
|
-
params = null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (params && typeof params === "object") {
|
|
72
|
-
if (Array.isArray(params)) {
|
|
73
|
-
for (const entry of params) {
|
|
74
|
-
if (!entry || typeof entry !== "object") continue;
|
|
75
|
-
const e = entry;
|
|
76
|
-
const name = String(e.name ?? e.valueName ?? "").trim().toLowerCase();
|
|
77
|
-
if (name !== "tornado") continue;
|
|
78
|
-
const val = e.value ?? e.values;
|
|
79
|
-
const vals = Array.isArray(val) ? val : val != null ? [val] : [];
|
|
80
|
-
for (const x of vals) {
|
|
81
|
-
if (String(x).trim().toLowerCase() === "observed") return true;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
const rec = params;
|
|
86
|
-
for (const key of Object.keys(rec)) {
|
|
87
|
-
const kl = key.trim().toLowerCase();
|
|
88
|
-
if (kl === "tornado") {
|
|
89
|
-
const v = rec[key];
|
|
90
|
-
const vals = Array.isArray(v) ? v : v != null ? [v] : [];
|
|
91
|
-
for (const x of vals) {
|
|
92
|
-
if (String(x).trim().toLowerCase() === "observed") return true;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (kl === "tornado_detection") {
|
|
96
|
-
const u = normalizeNwsWarningTagText(String(rec[key] ?? ""));
|
|
97
|
-
if (u.includes("OBSERVED") || u.includes("REPORTED") || u.includes("PUBLIC REPORTED")) {
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
function resolveNwsWarningCustomizationKey(baseCanonicalEvent, properties) {
|
|
107
|
-
if (baseCanonicalEvent === BASE_TORNADO) {
|
|
108
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
109
|
-
const torThreat = getNwsDamageThreatFromTags(BASE_TORNADO, properties, blob);
|
|
110
|
-
if (torThreat === "Catastrophic") return "Tornado Warning (Emergency)";
|
|
111
|
-
if (torThreat === "Considerable") return "Tornado Warning (PDS)";
|
|
112
|
-
if (hasTornadoObservedTag(blob)) return "Tornado Warning (Observed)";
|
|
113
|
-
const tornadoLine = getNwsTornadoThreatFromTags(properties, blob);
|
|
114
|
-
if (tornadoLine === "Observed") return "Tornado Warning (Observed)";
|
|
115
|
-
if (tornadoLine != null && tornadoLine.toLowerCase() !== "observed" && !tornadoLine.toLowerCase().includes("observed")) {
|
|
116
|
-
return BASE_TORNADO;
|
|
117
|
-
}
|
|
118
|
-
if (hasTornadoObservedFromExplicitFeatureProperties(properties)) return "Tornado Warning (Observed)";
|
|
119
|
-
if (hasTornadoObservedFromStructuredCap(properties)) return "Tornado Warning (Observed)";
|
|
120
|
-
return BASE_TORNADO;
|
|
121
|
-
}
|
|
122
|
-
if (baseCanonicalEvent === BASE_SEVERE) {
|
|
123
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
124
|
-
const svrThreat = getNwsDamageThreatFromTags(BASE_SEVERE, properties, blob);
|
|
125
|
-
if (svrThreat === "Destructive") return "Severe Thunderstorm Warning (Destructive)";
|
|
126
|
-
if (svrThreat === "Considerable") return "Severe Thunderstorm Warning (Considerable)";
|
|
127
|
-
return BASE_SEVERE;
|
|
128
|
-
}
|
|
129
|
-
if (baseCanonicalEvent === BASE_FLASH_FLOOD) {
|
|
130
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
131
|
-
const ffThreat = getNwsDamageThreatFromTags(BASE_FLASH_FLOOD, properties, blob);
|
|
132
|
-
if (ffThreat === "Catastrophic") return "Flash Flood Warning (Emergency)";
|
|
133
|
-
if (ffThreat === "Considerable") return "Flash Flood Warning (Considerable)";
|
|
134
|
-
return BASE_FLASH_FLOOD;
|
|
135
|
-
}
|
|
136
|
-
return baseCanonicalEvent;
|
|
137
|
-
}
|
|
138
|
-
var FLASH_FLOOD_SOURCE_LABELS = [
|
|
139
|
-
["DOPPLER RADAR INDICATED", "Doppler radar indicated"],
|
|
140
|
-
["RADAR AND GAUGE INDICATED", "Radar and gauge indicated"],
|
|
141
|
-
["RADAR INDICATED", "Radar indicated"],
|
|
142
|
-
["GAUGE INDICATED", "Gauge indicated"],
|
|
143
|
-
["OBSERVED", "Observed"],
|
|
144
|
-
["REPORTED", "Reported"]
|
|
145
|
-
];
|
|
146
|
-
function getNwsFlashFloodSourceIndication(properties) {
|
|
147
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
148
|
-
let start = 0;
|
|
149
|
-
while (start < blob.length) {
|
|
150
|
-
const i = blob.indexOf("FLASH FLOOD", start);
|
|
151
|
-
if (i === -1) break;
|
|
152
|
-
const seg = blob.slice(i);
|
|
153
|
-
if (seg.startsWith("FLASH FLOOD DAMAGE")) {
|
|
154
|
-
start = i + 11;
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
const window = seg.slice(0, 120);
|
|
158
|
-
for (const [needle, label] of FLASH_FLOOD_SOURCE_LABELS) {
|
|
159
|
-
if (window.includes(needle)) return label;
|
|
160
|
-
}
|
|
161
|
-
start = i + 11;
|
|
162
|
-
}
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
var DAM_FAILURE_STATUS_LABELS = [
|
|
166
|
-
["NOT EXPECTED", "Not expected"],
|
|
167
|
-
["IMMINENT", "Imminent"],
|
|
168
|
-
["POSSIBLE", "Possible"],
|
|
169
|
-
["OBSERVED", "Observed"]
|
|
170
|
-
];
|
|
171
|
-
function getNwsFlashFloodDamFailureTag(properties) {
|
|
172
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
173
|
-
const prefix = "DAM FAILURE";
|
|
174
|
-
let start = 0;
|
|
175
|
-
while (start < blob.length) {
|
|
176
|
-
const idx = blob.indexOf(prefix, start);
|
|
177
|
-
if (idx === -1) break;
|
|
178
|
-
const tail = blob.slice(idx + prefix.length).trimStart();
|
|
179
|
-
const window = tail.slice(0, 48);
|
|
180
|
-
for (const [needle, label] of DAM_FAILURE_STATUS_LABELS) {
|
|
181
|
-
if (window.includes(needle)) return label;
|
|
182
|
-
}
|
|
183
|
-
start = idx + 1;
|
|
184
|
-
}
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
function capitalizeThreatWord(word) {
|
|
188
|
-
return word.charAt(0) + word.slice(1).toLowerCase();
|
|
189
|
-
}
|
|
190
|
-
var NWS_DAMAGE_THREAT_ORDER = ["CATASTROPHIC", "DESTRUCTIVE", "CONSIDERABLE"];
|
|
191
|
-
function parseNwsDamageThreatTierAfterKey(blob, prefix) {
|
|
192
|
-
let endSearch = blob.length;
|
|
193
|
-
while (endSearch >= 0) {
|
|
194
|
-
const idx = blob.lastIndexOf(prefix, endSearch);
|
|
195
|
-
if (idx === -1) return null;
|
|
196
|
-
const window = blob.slice(idx + prefix.length, idx + prefix.length + 160);
|
|
197
|
-
for (const t of NWS_DAMAGE_THREAT_ORDER) {
|
|
198
|
-
if (window.includes(t)) return capitalizeThreatWord(t);
|
|
199
|
-
}
|
|
200
|
-
endSearch = idx - 1;
|
|
201
|
-
}
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
function compactPropertyKeyForMatch(key) {
|
|
205
|
-
return String(key).toLowerCase().replace(/[\s_-]+/g, "");
|
|
206
|
-
}
|
|
207
|
-
function isConvectiveStructuredDamageThreatPropertyKey(key) {
|
|
208
|
-
const c = compactPropertyKeyForMatch(key);
|
|
209
|
-
return c === "damagethreat" || c === "thunderstormdamagethreat" || c === "svrdamagethreat" || c === "tstmthreat" || c === "tstmdamagethreat" || c === "tornadodamagethreat" || c === "tordamagethreat" || c === "flashflooddamagethreat" || c === "ffwdamagethreat" || c === "flooddamagethreat";
|
|
210
|
-
}
|
|
211
|
-
function parseDamageThreatTierFromScalar(value) {
|
|
212
|
-
if (value == null) return null;
|
|
213
|
-
const u = normalizeNwsWarningTagText(String(value));
|
|
214
|
-
if (!u) return null;
|
|
215
|
-
for (const t of NWS_DAMAGE_THREAT_ORDER) {
|
|
216
|
-
if (u.includes(t)) return capitalizeThreatWord(t);
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
function getNwsDamageThreatFromStructuredProperties(properties) {
|
|
221
|
-
if (!properties) return null;
|
|
222
|
-
for (const [key, val] of Object.entries(properties)) {
|
|
223
|
-
if (!isConvectiveStructuredDamageThreatPropertyKey(key)) continue;
|
|
224
|
-
const tier = parseDamageThreatTierFromScalar(val);
|
|
225
|
-
if (tier) return tier;
|
|
226
|
-
}
|
|
227
|
-
let params = properties.parameters;
|
|
228
|
-
if (typeof params === "string") {
|
|
229
|
-
try {
|
|
230
|
-
params = JSON.parse(params);
|
|
231
|
-
} catch {
|
|
232
|
-
params = null;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (params && typeof params === "object" && !Array.isArray(params)) {
|
|
236
|
-
for (const [key, val] of Object.entries(params)) {
|
|
237
|
-
if (!isConvectiveStructuredDamageThreatPropertyKey(key)) continue;
|
|
238
|
-
const tier = parseDamageThreatTierFromScalar(val);
|
|
239
|
-
if (tier) return tier;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
function getNwsDamageThreatFromTags(baseCanonicalEvent, properties, precomputedBlob) {
|
|
245
|
-
const blob = precomputedBlob ?? normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
246
|
-
let prefix = null;
|
|
247
|
-
if (baseCanonicalEvent === BASE_FLASH_FLOOD) prefix = "FLASH FLOOD DAMAGE THREAT";
|
|
248
|
-
else if (baseCanonicalEvent === BASE_SEVERE) prefix = "THUNDERSTORM DAMAGE THREAT";
|
|
249
|
-
else if (baseCanonicalEvent === BASE_TORNADO) prefix = "TORNADO DAMAGE THREAT";
|
|
250
|
-
else return null;
|
|
251
|
-
const fromTags = parseNwsDamageThreatTierAfterKey(blob, prefix);
|
|
252
|
-
if (fromTags != null) return fromTags;
|
|
253
|
-
if (baseCanonicalEvent === BASE_SEVERE || baseCanonicalEvent === BASE_TORNADO || baseCanonicalEvent === BASE_FLASH_FLOOD) {
|
|
254
|
-
return getNwsDamageThreatFromStructuredProperties(properties);
|
|
255
|
-
}
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
function humanizeNwsHailWindTagValue(raw) {
|
|
259
|
-
return raw.replace(/\bIN\b/g, "in").replace(/\bMPH\b/g, "mph").replace(/\bKT\b/g, "kt").replace(/\bCM\b/g, "cm");
|
|
260
|
-
}
|
|
261
|
-
function shouldDisplayNwsParsedMaxHailSize(maxHail) {
|
|
262
|
-
if (maxHail == null) return false;
|
|
263
|
-
const s = String(maxHail).trim();
|
|
264
|
-
if (!s) return false;
|
|
265
|
-
const compact = s.toLowerCase().replace(/\s+/g, " ");
|
|
266
|
-
if (/^0(\.0+)?$/.test(compact)) return false;
|
|
267
|
-
if (/^0(\.0+)?\s*("|''|in|inch|inches)$/.test(compact)) return false;
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
270
|
-
var NWS_NARRATIVE_SOURCE_END_MARKERS = [
|
|
271
|
-
" IMPACT ",
|
|
272
|
-
" HAZARD ",
|
|
273
|
-
" PRECAUTIONARY",
|
|
274
|
-
" PRECAUTIONARY/",
|
|
275
|
-
" LAT ",
|
|
276
|
-
" TIME MOT LOC",
|
|
277
|
-
" $$"
|
|
278
|
-
];
|
|
279
|
-
function getNwsNarrativeSourceIndication(properties) {
|
|
280
|
-
const rawBlob = nwsAlertTagBlob(properties);
|
|
281
|
-
if (!rawBlob.trim()) return null;
|
|
282
|
-
const blob = normalizeNwsWarningTagText(rawBlob);
|
|
283
|
-
const re = /\bSOURCE\b/;
|
|
284
|
-
const m = blob.match(re);
|
|
285
|
-
if (!m || m.index == null) return null;
|
|
286
|
-
let tail = blob.slice(m.index + "SOURCE".length).trimStart();
|
|
287
|
-
let end = tail.length;
|
|
288
|
-
for (const marker of NWS_NARRATIVE_SOURCE_END_MARKERS) {
|
|
289
|
-
const j = tail.indexOf(marker);
|
|
290
|
-
if (j !== -1 && j < end) end = j;
|
|
291
|
-
}
|
|
292
|
-
const fragment = tail.slice(0, end).trim().replace(/\.$/, "").trim();
|
|
293
|
-
if (!fragment) return null;
|
|
294
|
-
for (const [needle, label] of FLASH_FLOOD_SOURCE_LABELS) {
|
|
295
|
-
if (fragment.includes(needle)) return label;
|
|
296
|
-
}
|
|
297
|
-
const fromThreat = humanizeNwsHailWindThreatValue(fragment);
|
|
298
|
-
if (fromThreat) return fromThreat;
|
|
299
|
-
return fragment.toLowerCase().replace(/\b[a-z]/g, (c) => c.toUpperCase());
|
|
300
|
-
}
|
|
301
|
-
function sliceTagValueAfterKey(blob, key, endKeys) {
|
|
302
|
-
const idx = blob.indexOf(key);
|
|
303
|
-
if (idx === -1) return null;
|
|
304
|
-
let tail = blob.slice(idx + key.length).replace(/^[\s.]+/, "");
|
|
305
|
-
let end = tail.length;
|
|
306
|
-
for (const ek of endKeys) {
|
|
307
|
-
const j = tail.indexOf(ek);
|
|
308
|
-
if (j !== -1 && j < end) end = j;
|
|
309
|
-
}
|
|
310
|
-
const val = tail.slice(0, end).trim();
|
|
311
|
-
return val || null;
|
|
312
|
-
}
|
|
313
|
-
function nwsHailWindTagFooterBlob(blob) {
|
|
314
|
-
const timeMot = blob.lastIndexOf("TIME MOT LOC");
|
|
315
|
-
if (timeMot !== -1) return blob.slice(timeMot);
|
|
316
|
-
const dmg = Math.max(
|
|
317
|
-
blob.lastIndexOf("THUNDERSTORM DAMAGE THREAT"),
|
|
318
|
-
blob.lastIndexOf("TORNADO DAMAGE THREAT")
|
|
319
|
-
);
|
|
320
|
-
if (dmg !== -1) return blob.slice(dmg);
|
|
321
|
-
return blob;
|
|
322
|
-
}
|
|
323
|
-
var TORNADO_TAG_LINE_END_KEYS = [
|
|
324
|
-
"HAIL THREAT",
|
|
325
|
-
"WIND THREAT",
|
|
326
|
-
"MAX HAIL SIZE",
|
|
327
|
-
"MAX WIND GUST",
|
|
328
|
-
"TORNADO DAMAGE THREAT",
|
|
329
|
-
"THUNDERSTORM DAMAGE THREAT",
|
|
330
|
-
"FLASH FLOOD DAMAGE THREAT",
|
|
331
|
-
"$$"
|
|
332
|
-
];
|
|
333
|
-
var TORNADO_TAG_UPPER_VALUE_PHRASES = [
|
|
334
|
-
"DOPPLER RADAR INDICATED",
|
|
335
|
-
"RADAR AND GAUGE INDICATED",
|
|
336
|
-
"RADAR INDICATED",
|
|
337
|
-
"GAUGE INDICATED",
|
|
338
|
-
"PUBLIC REPORTED",
|
|
339
|
-
"OBSERVED",
|
|
340
|
-
"REPORTED",
|
|
341
|
-
"LIKELY",
|
|
342
|
-
"POSSIBLE",
|
|
343
|
-
"PENDING"
|
|
344
|
-
];
|
|
345
|
-
function extractTornadoTagValueUpperFromFooter(footer) {
|
|
346
|
-
const inner = TORNADO_TAG_UPPER_VALUE_PHRASES.map(
|
|
347
|
-
(n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
348
|
-
).join("|");
|
|
349
|
-
const re = new RegExp(`TORNADO(?!\\s+DAMAGE THREAT)(?:\\s|\\.)+(${inner})\\b`, "gi");
|
|
350
|
-
let last = null;
|
|
351
|
-
let m;
|
|
352
|
-
while ((m = re.exec(footer)) !== null) {
|
|
353
|
-
last = m[1];
|
|
354
|
-
}
|
|
355
|
-
return last;
|
|
356
|
-
}
|
|
357
|
-
function sliceTornadoTagLineRaw(footer) {
|
|
358
|
-
const key = "TORNADO";
|
|
359
|
-
let searchFrom = 0;
|
|
360
|
-
while (searchFrom < footer.length) {
|
|
361
|
-
const idx = footer.indexOf(key, searchFrom);
|
|
362
|
-
if (idx === -1) return null;
|
|
363
|
-
const afterKey = footer.slice(idx + key.length);
|
|
364
|
-
const trimmedStart = afterKey.trimStart();
|
|
365
|
-
if (trimmedStart.startsWith("DAMAGE THREAT")) {
|
|
366
|
-
searchFrom = idx + key.length;
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
let tail = afterKey.replace(/^[\s.]+/, "");
|
|
370
|
-
let end = tail.length;
|
|
371
|
-
for (const ek of TORNADO_TAG_LINE_END_KEYS) {
|
|
372
|
-
const j = tail.indexOf(ek);
|
|
373
|
-
if (j !== -1 && j < end) end = j;
|
|
374
|
-
}
|
|
375
|
-
let val = tail.slice(0, end).trim();
|
|
376
|
-
if (!val) {
|
|
377
|
-
searchFrom = idx + key.length;
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
if (val.length > 64) {
|
|
381
|
-
for (const needle of TORNADO_TAG_UPPER_VALUE_PHRASES) {
|
|
382
|
-
if (val.startsWith(needle)) return needle;
|
|
383
|
-
}
|
|
384
|
-
return null;
|
|
385
|
-
}
|
|
386
|
-
return val;
|
|
387
|
-
}
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
var HAIL_WIND_THREAT_VALUE_LABELS = [
|
|
391
|
-
["DOPPLER RADAR INDICATED", "Doppler radar indicated"],
|
|
392
|
-
["RADAR AND GAUGE INDICATED", "Radar and gauge indicated"],
|
|
393
|
-
["RADAR INDICATED", "Radar indicated"],
|
|
394
|
-
["GAUGE INDICATED", "Gauge indicated"],
|
|
395
|
-
["PUBLIC REPORTED", "Public reported"],
|
|
396
|
-
["OBSERVED", "Observed"],
|
|
397
|
-
["REPORTED", "Reported"],
|
|
398
|
-
["LIKELY", "Likely"],
|
|
399
|
-
["POSSIBLE", "Possible"],
|
|
400
|
-
["PENDING", "Pending"]
|
|
401
|
-
];
|
|
402
|
-
function humanizeNwsHailWindThreatValue(rawUpper) {
|
|
403
|
-
const s = rawUpper.trim();
|
|
404
|
-
if (!s) return null;
|
|
405
|
-
for (const [needle, label] of HAIL_WIND_THREAT_VALUE_LABELS) {
|
|
406
|
-
if (s === needle) return label;
|
|
407
|
-
if (s.startsWith(`${needle} `)) return label;
|
|
408
|
-
}
|
|
409
|
-
if (s.length > 96) {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
return s.toLowerCase().replace(/\b[a-z]/g, (c) => c.toUpperCase());
|
|
413
|
-
}
|
|
414
|
-
function getNwsTornadoThreatFromTags(properties, precomputedBlob) {
|
|
415
|
-
const blob = precomputedBlob ?? normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
416
|
-
if (!blob) return null;
|
|
417
|
-
const footer = nwsHailWindTagFooterBlob(blob);
|
|
418
|
-
const fromPhrase = extractTornadoTagValueUpperFromFooter(footer);
|
|
419
|
-
if (fromPhrase != null) {
|
|
420
|
-
const h = humanizeNwsHailWindThreatValue(fromPhrase);
|
|
421
|
-
if (h) return h;
|
|
422
|
-
}
|
|
423
|
-
const raw = sliceTornadoTagLineRaw(footer);
|
|
424
|
-
if (raw == null) return null;
|
|
425
|
-
return humanizeNwsHailWindThreatValue(raw);
|
|
426
|
-
}
|
|
427
|
-
var TORNADO_SUBTYPE_CHIP = {
|
|
428
|
-
"Tornado Warning (Observed)": "Observed",
|
|
429
|
-
"Tornado Warning (PDS)": "PDS",
|
|
430
|
-
"Tornado Warning (Emergency)": "Emergency"
|
|
431
|
-
};
|
|
432
|
-
function getNwsTornadoThreatChipForPopup(properties) {
|
|
433
|
-
const fromTags = getNwsTornadoThreatFromTags(properties);
|
|
434
|
-
if (fromTags != null) return fromTags;
|
|
435
|
-
const resolved = resolveNwsWarningCustomizationKey(BASE_TORNADO, properties);
|
|
436
|
-
const fromResolve = TORNADO_SUBTYPE_CHIP[resolved];
|
|
437
|
-
if (fromResolve != null) return fromResolve;
|
|
438
|
-
const ev = properties?.event_name != null ? String(properties.event_name).trim() : "";
|
|
439
|
-
if (ev && TORNADO_SUBTYPE_CHIP[ev]) return TORNADO_SUBTYPE_CHIP[ev];
|
|
440
|
-
if (resolved === BASE_TORNADO) return "Radar indicated";
|
|
441
|
-
return null;
|
|
442
|
-
}
|
|
443
|
-
function getNwsHailThreatFromTags(properties) {
|
|
444
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
445
|
-
const footer = nwsHailWindTagFooterBlob(blob);
|
|
446
|
-
const raw = sliceTagValueAfterKey(footer, "HAIL THREAT", [
|
|
447
|
-
"MAX HAIL SIZE",
|
|
448
|
-
"WIND THREAT",
|
|
449
|
-
"MAX WIND GUST",
|
|
450
|
-
"$$"
|
|
451
|
-
]);
|
|
452
|
-
if (raw == null) return null;
|
|
453
|
-
return humanizeNwsHailWindThreatValue(raw);
|
|
454
|
-
}
|
|
455
|
-
function getNwsWindThreatFromTags(properties) {
|
|
456
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
457
|
-
const footer = nwsHailWindTagFooterBlob(blob);
|
|
458
|
-
const raw = sliceTagValueAfterKey(footer, "WIND THREAT", ["MAX WIND GUST", "$$"]);
|
|
459
|
-
if (raw == null) return null;
|
|
460
|
-
return humanizeNwsHailWindThreatValue(raw);
|
|
461
|
-
}
|
|
462
|
-
function getNwsMaxHailSizeFromTags(properties) {
|
|
463
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
464
|
-
const raw = sliceTagValueAfterKey(blob, "MAX HAIL SIZE", [
|
|
465
|
-
"WIND THREAT",
|
|
466
|
-
"HAIL THREAT",
|
|
467
|
-
"MAX WIND GUST",
|
|
468
|
-
"$$"
|
|
469
|
-
]);
|
|
470
|
-
if (raw == null) return null;
|
|
471
|
-
return humanizeNwsHailWindTagValue(raw);
|
|
472
|
-
}
|
|
473
|
-
function getNwsMaxWindGustFromTags(properties) {
|
|
474
|
-
const blob = normalizeNwsWarningTagText(nwsAlertTagBlob(properties));
|
|
475
|
-
const raw = sliceTagValueAfterKey(blob, "MAX WIND GUST", ["$$"]);
|
|
476
|
-
if (raw == null) return null;
|
|
477
|
-
return humanizeNwsHailWindTagValue(raw);
|
|
478
|
-
}
|
|
479
|
-
export {
|
|
480
|
-
getNwsDamageThreatFromTags,
|
|
481
|
-
getNwsFlashFloodDamFailureTag,
|
|
482
|
-
getNwsFlashFloodSourceIndication,
|
|
483
|
-
getNwsHailThreatFromTags,
|
|
484
|
-
getNwsMaxHailSizeFromTags,
|
|
485
|
-
getNwsMaxWindGustFromTags,
|
|
486
|
-
getNwsNarrativeSourceIndication,
|
|
487
|
-
getNwsTornadoThreatChipForPopup,
|
|
488
|
-
getNwsTornadoThreatFromTags,
|
|
489
|
-
getNwsWindThreatFromTags,
|
|
490
|
-
normalizeNwsWarningTagText,
|
|
491
|
-
resolveNwsWarningCustomizationKey,
|
|
492
|
-
shouldDisplayNwsParsedMaxHailSize
|
|
493
|
-
};
|