@aguacerowx/mapsgl 0.0.49 → 0.0.51
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/package.json +2 -2
- package/src/NexradWeatherController.js +26 -7
- package/src/NwsWatchesWarningsOverlay.js +858 -789
- package/src/WeatherLayerManager.js +61 -298
- package/src/nwsAlertsSupport.js +160 -7
|
@@ -10,48 +10,6 @@ import WorkerPool from './WorkerPool.js';
|
|
|
10
10
|
|
|
11
11
|
import { DEFAULT_BASIS_BASE_URL } from './defaultBasisBaseUrl.js';
|
|
12
12
|
|
|
13
|
-
const DEBUG_NS = '[WeatherLayerManager:debug]';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Redact secrets for console output.
|
|
17
|
-
* @param {object} options
|
|
18
|
-
*/
|
|
19
|
-
function _debugSanitizeOptions(options) {
|
|
20
|
-
if (!options || typeof options !== 'object') return options;
|
|
21
|
-
const o = { ...options };
|
|
22
|
-
if (typeof o.apiKey === 'string' && o.apiKey.length > 0) {
|
|
23
|
-
o.apiKey = `${o.apiKey.slice(0, 4)}…(${o.apiKey.length} chars)`;
|
|
24
|
-
}
|
|
25
|
-
return o;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Compact summary of timestamp / hour arrays (MRMS timeline debugging).
|
|
30
|
-
* @param {unknown[]} arr
|
|
31
|
-
*/
|
|
32
|
-
function _debugSummarizeNumericSeries(arr) {
|
|
33
|
-
if (!Array.isArray(arr) || arr.length === 0) {
|
|
34
|
-
return { length: 0, min: null, max: null, spanSec: null, head: [], tail: undefined };
|
|
35
|
-
}
|
|
36
|
-
const nums = arr.map((x) => Number(x)).filter((n) => !Number.isNaN(n));
|
|
37
|
-
if (nums.length === 0) {
|
|
38
|
-
return { length: 0, min: null, max: null, spanSec: null, head: [], tail: undefined };
|
|
39
|
-
}
|
|
40
|
-
const sorted = [...nums].sort((a, b) => a - b);
|
|
41
|
-
const min = sorted[0];
|
|
42
|
-
const max = sorted[sorted.length - 1];
|
|
43
|
-
const head = sorted.slice(0, Math.min(8, sorted.length));
|
|
44
|
-
const tail = sorted.length > 16 ? sorted.slice(-8) : [];
|
|
45
|
-
return {
|
|
46
|
-
length: sorted.length,
|
|
47
|
-
min,
|
|
48
|
-
max,
|
|
49
|
-
spanSec: max != null && min != null ? max - min : null,
|
|
50
|
-
head,
|
|
51
|
-
tail: tail.length ? tail : undefined,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
13
|
function findLatestModelRun(modelsData, modelName) {
|
|
56
14
|
const model = modelsData?.[modelName];
|
|
57
15
|
if (!model) return null;
|
|
@@ -77,7 +35,7 @@ function findLatestModelRun(modelsData, modelName) {
|
|
|
77
35
|
* @param {string} [options.belowID] - Style layer id to insert Aguacero weather layers **below** (default `AML_-_terrain` when present). Alias of `weatherBeforeLayerId`.
|
|
78
36
|
* @param {string} [options.weatherBeforeLayerId] - Same as `belowID`.
|
|
79
37
|
* @param {string} [options.nexradLayerId] - Override Mapbox id for the NEXRAD custom layer (default: derived from `layerId`).
|
|
80
|
-
* @param {boolean} [options.
|
|
38
|
+
* @param {boolean} [options.mrmsTimelineLog] - When `true`, logs MRMS duration / raw vs filtered timeline counts (prefix `[WeatherLayerManager MRMS]`). Off by default.
|
|
81
39
|
* @param {string} [options.basisBaseUrl] - URL prefix for satellite KTX2 Basis transcoder assets (`basis_transcoder.js`, `basis_transcoder.wasm`). Defaults to jsDelivr for the published package version; override (e.g. `/basis/`) for strict CSP or offline.
|
|
82
40
|
*/
|
|
83
41
|
export class WeatherLayerManager extends EventEmitter {
|
|
@@ -85,10 +43,10 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
85
43
|
super();
|
|
86
44
|
if (!map) throw new Error('A Mapbox GL map instance is required.');
|
|
87
45
|
this.map = map;
|
|
88
|
-
/** @private
|
|
89
|
-
this.
|
|
90
|
-
/** @private
|
|
91
|
-
this.
|
|
46
|
+
/** @private MRMS-only timeline diagnostics (duration, raw vs filtered counts). */
|
|
47
|
+
this._mrmsTimelineLog = options.mrmsTimelineLog === true;
|
|
48
|
+
/** @private Dedupes repeated MRMS timeline log lines. */
|
|
49
|
+
this._mrmsLogKey = '';
|
|
92
50
|
this.layerId =
|
|
93
51
|
options.layerId ||
|
|
94
52
|
options.id ||
|
|
@@ -159,30 +117,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
159
117
|
// 1. CREATE an instance of the core engine
|
|
160
118
|
this.core = new AguaceroCore(options);
|
|
161
119
|
|
|
162
|
-
if (this._debug) {
|
|
163
|
-
const layerOpts = options.layerOptions || {};
|
|
164
|
-
console.log(DEBUG_NS, 'constructor', {
|
|
165
|
-
layerId: this.layerId,
|
|
166
|
-
nexradLayerId: this._nexradLayerId,
|
|
167
|
-
mapLoaded: typeof this.map?.loaded === 'function' ? this.map.loaded() : undefined,
|
|
168
|
-
styleLoaded: this.map?.isStyleLoaded?.() ?? undefined,
|
|
169
|
-
weatherBeforeLayerId: this._weatherBeforeLayerId,
|
|
170
|
-
options: _debugSanitizeOptions(options),
|
|
171
|
-
layerOptions: layerOpts,
|
|
172
|
-
coreStateSnapshot: {
|
|
173
|
-
isMRMS: this.core.state?.isMRMS,
|
|
174
|
-
isSatellite: this.core.state?.isSatellite,
|
|
175
|
-
isNexrad: this.core.state?.isNexrad,
|
|
176
|
-
model: this.core.state?.model,
|
|
177
|
-
variable: this.core.state?.variable,
|
|
178
|
-
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
179
|
-
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
180
|
-
nexradDurationValue: this.core.state?.nexradDurationValue,
|
|
181
|
-
satelliteDurationValue: this.core.state?.satelliteDurationValue,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
120
|
// 2. LISTEN for events from the core engine
|
|
187
121
|
this.core.on('state:change', (newState) => {
|
|
188
122
|
this._lastEmittedState = newState;
|
|
@@ -203,17 +137,44 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
203
137
|
}
|
|
204
138
|
|
|
205
139
|
/**
|
|
206
|
-
*
|
|
207
|
-
* @param {
|
|
208
|
-
* @param {object} [data]
|
|
140
|
+
* Logs MRMS timeline facts when `options.mrmsTimelineLog` is enabled (once per distinct snapshot).
|
|
141
|
+
* @param {object} state
|
|
209
142
|
*/
|
|
210
|
-
|
|
211
|
-
if (!this.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
143
|
+
_logMrmsTimelineIfEnabled(state) {
|
|
144
|
+
if (!this._mrmsTimelineLog || !state.isMRMS || !state.variable) return;
|
|
145
|
+
const raw = this.core.mrmsStatus?.[state.variable];
|
|
146
|
+
const rawLen = Array.isArray(raw) ? raw.length : 0;
|
|
147
|
+
const avail = state.availableTimestamps || [];
|
|
148
|
+
const filteredLen = avail.length;
|
|
149
|
+
let spanH = null;
|
|
150
|
+
let minT = null;
|
|
151
|
+
let maxT = null;
|
|
152
|
+
if (filteredLen) {
|
|
153
|
+
const nums = avail.map(Number).filter(Number.isFinite).sort((a, b) => a - b);
|
|
154
|
+
minT = nums[0];
|
|
155
|
+
maxT = nums[nums.length - 1];
|
|
156
|
+
spanH = (maxT - minT) / 3600;
|
|
157
|
+
}
|
|
158
|
+
const key = `${state.variable}|${state.mrmsDurationValue}|${state.mrmsTimestamp}|${rawLen}|${filteredLen}|${minT}|${maxT}`;
|
|
159
|
+
if (key === this._mrmsLogKey) return;
|
|
160
|
+
this._mrmsLogKey = key;
|
|
161
|
+
const rawSpanH =
|
|
162
|
+
rawLen > 1 && Array.isArray(raw)
|
|
163
|
+
? (Math.max(...raw.map(Number)) - Math.min(...raw.map(Number))) / 3600
|
|
164
|
+
: null;
|
|
165
|
+
console.log('[WeatherLayerManager MRMS]', {
|
|
166
|
+
variable: state.variable,
|
|
167
|
+
durationSettingHours: state.mrmsDurationValue,
|
|
168
|
+
selectedUnix: state.mrmsTimestamp,
|
|
169
|
+
rawStatusScanCount: rawLen,
|
|
170
|
+
rawWallClockSpanHoursApprox: rawSpanH != null ? Number(rawSpanH.toFixed(2)) : null,
|
|
171
|
+
filteredTimelineCount: filteredLen,
|
|
172
|
+
filteredSpanHoursApprox: spanH != null ? Number(spanH.toFixed(2)) : null,
|
|
173
|
+
filteredWindow:
|
|
174
|
+
minT != null && maxT != null ? { minUnix: minT, maxUnix: maxT } : null,
|
|
175
|
+
note:
|
|
176
|
+
'Timeline cannot extend beyond scans returned by the MRMS status feed; widening "hours" only includes more of that list when older scans exist.',
|
|
177
|
+
});
|
|
217
178
|
}
|
|
218
179
|
|
|
219
180
|
/**
|
|
@@ -262,38 +223,11 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
262
223
|
* @private
|
|
263
224
|
*/
|
|
264
225
|
_handleStateChange(state) {
|
|
265
|
-
const seq = this._debug ? ++this._debugStateSeq : 0;
|
|
266
226
|
/** NEXRAD setup is async (`sites.show`, frame fetch). `finally` would run before the radar layer exists, so NWS fill stacks above terrain instead of under the custom layer — defer sync until the handler settles. */
|
|
267
227
|
let deferNwsSyncUntilNexradReady = false;
|
|
268
228
|
try {
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
? _debugSummarizeNumericSeries(state.availableTimestamps || [])
|
|
272
|
-
: null;
|
|
273
|
-
this._debugLog('state:change', {
|
|
274
|
-
seq,
|
|
275
|
-
mode: state.isSatellite
|
|
276
|
-
? 'satellite'
|
|
277
|
-
: state.isNexrad
|
|
278
|
-
? 'nexrad'
|
|
279
|
-
: state.isMRMS
|
|
280
|
-
? 'mrms'
|
|
281
|
-
: 'model',
|
|
282
|
-
model: state.model,
|
|
283
|
-
variable: state.variable,
|
|
284
|
-
runKey: `${state.model}-${state.date}-${state.run}-${state.variable}`,
|
|
285
|
-
timeKey: state.isMRMS
|
|
286
|
-
? (state.mrmsTimestamp == null ? null : Number(state.mrmsTimestamp))
|
|
287
|
-
: Number(state.forecastHour),
|
|
288
|
-
mrmsTimestamp: state.mrmsTimestamp,
|
|
289
|
-
mrmsDurationValue: state.mrmsDurationValue,
|
|
290
|
-
availableTimestampsSummary: tsSummary,
|
|
291
|
-
availableHoursLen: Array.isArray(state.availableHours) ? state.availableHours.length : 0,
|
|
292
|
-
shaderLayerRunKey: this.shaderLayer?.runKey ?? null,
|
|
293
|
-
currentLoadedTimeKey: this.currentLoadedTimeKey,
|
|
294
|
-
rebuildId: this.currentRebuildId,
|
|
295
|
-
initialGridLoadPending: this._initialGridLoadPending,
|
|
296
|
-
});
|
|
229
|
+
if (state.isMRMS) {
|
|
230
|
+
this._logMrmsTimelineIfEnabled(state);
|
|
297
231
|
}
|
|
298
232
|
|
|
299
233
|
if (state.isSatellite) {
|
|
@@ -325,21 +259,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
325
259
|
: Number(state.forecastHour);
|
|
326
260
|
const runKey = `${state.model}-${state.date}-${state.run}-${state.variable}`;
|
|
327
261
|
|
|
328
|
-
if (this._debug && state.isMRMS) {
|
|
329
|
-
this._debugLog('grid path (mrms/model)', {
|
|
330
|
-
seq,
|
|
331
|
-
prevMrmsDurationValue: prevMrmsDur,
|
|
332
|
-
mrmsDurationChanged,
|
|
333
|
-
willRebuild: !this.shaderLayer || this.shaderLayer.runKey !== runKey,
|
|
334
|
-
willUpdateData:
|
|
335
|
-
this.shaderLayer &&
|
|
336
|
-
this.shaderLayer.runKey === runKey &&
|
|
337
|
-
this.currentLoadedTimeKey !== timeKey,
|
|
338
|
-
shaderRunKey: this.shaderLayer?.runKey,
|
|
339
|
-
targetRunKey: runKey,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
|
|
343
262
|
if (!this.shaderLayer || this.shaderLayer.runKey !== runKey) {
|
|
344
263
|
this._rebuildLayerAndPreload(state);
|
|
345
264
|
} else if (this.currentLoadedTimeKey !== timeKey) {
|
|
@@ -349,12 +268,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
349
268
|
timeKey === this._rebuildTargetTimeKey;
|
|
350
269
|
if (!duplicateBeforeFirstPaint) {
|
|
351
270
|
this._updateLayerData(state);
|
|
352
|
-
} else if (this._debug) {
|
|
353
|
-
this._debugLog('skip _updateLayerData (duplicate before first paint)', {
|
|
354
|
-
seq,
|
|
355
|
-
timeKey,
|
|
356
|
-
_rebuildTargetTimeKey: this._rebuildTargetTimeKey,
|
|
357
|
-
});
|
|
358
271
|
}
|
|
359
272
|
}
|
|
360
273
|
|
|
@@ -365,13 +278,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
365
278
|
}
|
|
366
279
|
|
|
367
280
|
if (state.isMRMS && this.shaderLayer && mrmsDurationChanged) {
|
|
368
|
-
if (this._debug) {
|
|
369
|
-
this._debugLog('_preloadAllTimeSteps (mrms duration changed)', {
|
|
370
|
-
seq,
|
|
371
|
-
from: prevMrmsDur,
|
|
372
|
-
to: state.mrmsDurationValue,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
281
|
this._preloadAllTimeSteps(state);
|
|
376
282
|
}
|
|
377
283
|
} finally {
|
|
@@ -418,12 +324,17 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
418
324
|
const enabled = masterEnabled && !isModelMode;
|
|
419
325
|
|
|
420
326
|
let timelineUnix = null;
|
|
327
|
+
/** Same list the core uses for the time slider — enables NWS “live edge” (wall clock at latest step) like aguacero-frontend. */
|
|
328
|
+
let timelineTimes = null;
|
|
421
329
|
if (state.isSatellite) {
|
|
422
330
|
timelineUnix = state.satelliteTimestamp == null ? null : Number(state.satelliteTimestamp);
|
|
331
|
+
timelineTimes = state.availableSatelliteTimestamps;
|
|
423
332
|
} else if (state.isNexrad) {
|
|
424
333
|
timelineUnix = state.nexradTimestamp == null ? null : Number(state.nexradTimestamp);
|
|
334
|
+
timelineTimes = state.availableNexradTimestamps;
|
|
425
335
|
} else if (state.isMRMS) {
|
|
426
336
|
timelineUnix = state.mrmsTimestamp == null ? null : Number(state.mrmsTimestamp);
|
|
337
|
+
timelineTimes = state.availableTimestamps;
|
|
427
338
|
}
|
|
428
339
|
|
|
429
340
|
if (!this._nwsOverlay) {
|
|
@@ -453,7 +364,7 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
453
364
|
...(wopt.activeOnlyRealtime !== undefined ? { activeOnlyRealtime: wopt.activeOnlyRealtime } : {}),
|
|
454
365
|
},
|
|
455
366
|
});
|
|
456
|
-
this._nwsOverlay.syncWithMode({ enabled, timelineUnix });
|
|
367
|
+
this._nwsOverlay.syncWithMode({ enabled, timelineUnix, timelineTimes });
|
|
457
368
|
}
|
|
458
369
|
|
|
459
370
|
/**
|
|
@@ -784,68 +695,10 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
784
695
|
// to the core engine. This keeps the API consistent for your users.
|
|
785
696
|
|
|
786
697
|
async initialize(options) {
|
|
787
|
-
const t0 =
|
|
788
|
-
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
789
|
-
? performance.now()
|
|
790
|
-
: Date.now();
|
|
791
|
-
this._debugLog('initialize:start', {
|
|
792
|
-
autoRefreshEnabled: this.autoRefreshEnabled,
|
|
793
|
-
autoRefreshIntervalSeconds: this.autoRefreshIntervalSeconds,
|
|
794
|
-
passedOptions: options && typeof options === 'object' ? { ...options } : options,
|
|
795
|
-
coreStateBefore: {
|
|
796
|
-
isMRMS: this.core.state?.isMRMS,
|
|
797
|
-
variable: this.core.state?.variable,
|
|
798
|
-
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
799
|
-
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
800
|
-
},
|
|
801
|
-
mrmsStatusVariableLen: this.core.state?.variable
|
|
802
|
-
? (this.core.mrmsStatus?.[this.core.state.variable]?.length ?? 'n/a')
|
|
803
|
-
: 'n/a',
|
|
804
|
-
});
|
|
805
698
|
if (this.autoRefreshEnabled) {
|
|
806
699
|
this.setAutoRefresh(true, this.autoRefreshIntervalSeconds);
|
|
807
700
|
}
|
|
808
|
-
|
|
809
|
-
const result = await this.core.initialize({ ...options, autoRefresh: false });
|
|
810
|
-
const t1 =
|
|
811
|
-
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
812
|
-
? performance.now()
|
|
813
|
-
: Date.now();
|
|
814
|
-
this._debugLog('initialize:done', {
|
|
815
|
-
elapsedMs: Math.round(t1 - t0),
|
|
816
|
-
coreStateAfter: {
|
|
817
|
-
isMRMS: this.core.state?.isMRMS,
|
|
818
|
-
variable: this.core.state?.variable,
|
|
819
|
-
model: this.core.state?.model,
|
|
820
|
-
date: this.core.state?.date,
|
|
821
|
-
run: this.core.state?.run,
|
|
822
|
-
forecastHour: this.core.state?.forecastHour,
|
|
823
|
-
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
824
|
-
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
825
|
-
},
|
|
826
|
-
availableTimestampsSummary: _debugSummarizeNumericSeries(
|
|
827
|
-
this._lastEmittedState?.availableTimestamps || [],
|
|
828
|
-
),
|
|
829
|
-
availableHoursLen: Array.isArray(this._lastEmittedState?.availableHours)
|
|
830
|
-
? this._lastEmittedState.availableHours.length
|
|
831
|
-
: 0,
|
|
832
|
-
modelStatusLoaded: this.core.modelStatus != null,
|
|
833
|
-
mrmsStatusKeys:
|
|
834
|
-
this.core.mrmsStatus && typeof this.core.mrmsStatus === 'object'
|
|
835
|
-
? Object.keys(this.core.mrmsStatus).length
|
|
836
|
-
: 0,
|
|
837
|
-
mrmsStatusVariableLen: this.core.state?.variable
|
|
838
|
-
? (this.core.mrmsStatus?.[this.core.state.variable]?.length ?? 0)
|
|
839
|
-
: 0,
|
|
840
|
-
});
|
|
841
|
-
return result;
|
|
842
|
-
} catch (err) {
|
|
843
|
-
this._debugLog('initialize:error', {
|
|
844
|
-
message: err?.message || String(err),
|
|
845
|
-
stack: err?.stack,
|
|
846
|
-
});
|
|
847
|
-
throw err;
|
|
848
|
-
}
|
|
701
|
+
return this.core.initialize({ ...options, autoRefresh: false });
|
|
849
702
|
}
|
|
850
703
|
async setState(newState) { return this.core.setState(newState); }
|
|
851
704
|
play() { this.core.play(); }
|
|
@@ -892,14 +745,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
892
745
|
async setNexradProduct(product) {
|
|
893
746
|
return this.core.setNexradProduct(product);
|
|
894
747
|
}
|
|
895
|
-
/** @param {'level2'|'level3'} dataSource @param {string} product */
|
|
896
|
-
async setNexradProductMode(dataSource, product) {
|
|
897
|
-
return this.core.setNexradProductMode(dataSource, product);
|
|
898
|
-
}
|
|
899
|
-
/** @param {'level2'|'level3'} source */
|
|
900
|
-
async setNexradDataSource(source) {
|
|
901
|
-
return this.core.setNexradDataSource(source);
|
|
902
|
-
}
|
|
903
748
|
async setNexradTilt(tilt) {
|
|
904
749
|
return this.core.setNexradTilt(tilt);
|
|
905
750
|
}
|
|
@@ -983,17 +828,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
983
828
|
const out = (timeSteps || [])
|
|
984
829
|
.map(t => Number(t))
|
|
985
830
|
.filter(t => !Number.isNaN(t));
|
|
986
|
-
if (this._debug && state.isMRMS) {
|
|
987
|
-
this._debugLog('_collectNormalizedTimelineSteps', {
|
|
988
|
-
variable: state.variable,
|
|
989
|
-
mrmsDurationValue: state.mrmsDurationValue,
|
|
990
|
-
fromStateLen: fromState.length,
|
|
991
|
-
fromCoreLen: fromCore.length,
|
|
992
|
-
chosenSource: mrmsTimelineSource || (state.isMRMS ? 'n/a' : 'model'),
|
|
993
|
-
normalizedLen: out.length,
|
|
994
|
-
summary: _debugSummarizeNumericSeries(out),
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
831
|
return out;
|
|
998
832
|
}
|
|
999
833
|
|
|
@@ -1008,50 +842,27 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1008
842
|
if (mode === 'rebuild') {
|
|
1009
843
|
this._initialGridLoadPending = false;
|
|
1010
844
|
}
|
|
1011
|
-
this._debugLog('_runParallelGridFrameLoads:skip', {
|
|
1012
|
-
reason: !times.length ? 'no times' : 'no shaderLayer',
|
|
1013
|
-
mode,
|
|
1014
|
-
rebuildId,
|
|
1015
|
-
timesLen: times.length,
|
|
1016
|
-
});
|
|
1017
845
|
return;
|
|
1018
846
|
}
|
|
1019
847
|
|
|
1020
|
-
// Number(null) === 0
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
848
|
+
// Number(null) === 0 — treat null as NaN. For MRMS, when the active timestep is unset,
|
|
849
|
+
// prefer the **newest** scan in the timeline (matches AguaceroCore default); model grids
|
|
850
|
+
// keep using the first forecast hour in the list.
|
|
1023
851
|
const _rawFrameTime = state.isMRMS ? state.mrmsTimestamp : state.forecastHour;
|
|
1024
852
|
const currentFrameTime = _rawFrameTime == null ? NaN : Number(_rawFrameTime);
|
|
1025
|
-
/** When the active timestep is unset/NaN, paint the first timeline step (legacy single-fetch behavior). */
|
|
1026
853
|
let primaryTimeForRebuild = Number.NaN;
|
|
1027
854
|
if (mode === 'rebuild') {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
855
|
+
if (Number.isFinite(currentFrameTime)) {
|
|
856
|
+
primaryTimeForRebuild = currentFrameTime;
|
|
857
|
+
} else if (times.length > 0) {
|
|
858
|
+
const finite = times.map(Number).filter(Number.isFinite);
|
|
859
|
+
primaryTimeForRebuild = state.isMRMS ? Math.max(...finite) : Number(times[0]);
|
|
860
|
+
}
|
|
1031
861
|
}
|
|
1032
862
|
const tsKey = state.isMRMS ? 'mrmsTimestamp' : 'forecastHour';
|
|
1033
863
|
const gridModel = state.isMRMS ? 'mrms' : state.model;
|
|
1034
864
|
const { gridDef } = this.core._getGridCornersAndDef(gridModel);
|
|
1035
865
|
|
|
1036
|
-
this._debugLog('_runParallelGridFrameLoads', {
|
|
1037
|
-
mode,
|
|
1038
|
-
rebuildId,
|
|
1039
|
-
gridModel,
|
|
1040
|
-
timesLen: times.length,
|
|
1041
|
-
timesSummary: _debugSummarizeNumericSeries(times),
|
|
1042
|
-
currentFrameTime,
|
|
1043
|
-
primaryTimeForRebuild:
|
|
1044
|
-
mode === 'rebuild'
|
|
1045
|
-
? Number.isFinite(currentFrameTime)
|
|
1046
|
-
? currentFrameTime
|
|
1047
|
-
: times[0]
|
|
1048
|
-
: undefined,
|
|
1049
|
-
tsKey,
|
|
1050
|
-
gridNxNy: gridDef?.grid_params
|
|
1051
|
-
? { nx: gridDef.grid_params.nx, ny: gridDef.grid_params.ny }
|
|
1052
|
-
: null,
|
|
1053
|
-
});
|
|
1054
|
-
|
|
1055
866
|
times.forEach((time) => {
|
|
1056
867
|
const stateForTime = { ...state, [tsKey]: time };
|
|
1057
868
|
this.core._loadGridData(stateForTime)
|
|
@@ -1080,21 +891,10 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1080
891
|
: Number(this.core.state.mrmsTimestamp))
|
|
1081
892
|
: Number(this.core.state.forecastHour);
|
|
1082
893
|
if (coreTimeKey !== this._rebuildTargetTimeKey) {
|
|
1083
|
-
this._debugLog('_loadGridData:skip primary (core time drifted vs rebuild target)', {
|
|
1084
|
-
time,
|
|
1085
|
-
coreTimeKey,
|
|
1086
|
-
_rebuildTargetTimeKey: this._rebuildTargetTimeKey,
|
|
1087
|
-
rebuildId,
|
|
1088
|
-
});
|
|
1089
894
|
return;
|
|
1090
895
|
}
|
|
1091
896
|
}
|
|
1092
897
|
if (!grid?.data) {
|
|
1093
|
-
this._debugLog('_loadGridData:primary frame missing grid.data', {
|
|
1094
|
-
time,
|
|
1095
|
-
rebuildId,
|
|
1096
|
-
mode,
|
|
1097
|
-
});
|
|
1098
898
|
this._initialGridLoadPending = false;
|
|
1099
899
|
return;
|
|
1100
900
|
}
|
|
@@ -1127,17 +927,9 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1127
927
|
this.currentLoadedTimeKey = time;
|
|
1128
928
|
this.map.triggerRepaint();
|
|
1129
929
|
}
|
|
1130
|
-
} else if (this._debug && mode === 'append') {
|
|
1131
|
-
this._debugLog('_loadGridData:empty grid (append)', { time, mode, rebuildId });
|
|
1132
930
|
}
|
|
1133
931
|
})
|
|
1134
|
-
.catch((
|
|
1135
|
-
this._debugLog('_loadGridData:error', {
|
|
1136
|
-
time,
|
|
1137
|
-
mode,
|
|
1138
|
-
rebuildId,
|
|
1139
|
-
message: err?.message || String(err),
|
|
1140
|
-
});
|
|
932
|
+
.catch(() => {
|
|
1141
933
|
if (
|
|
1142
934
|
mode === 'rebuild' &&
|
|
1143
935
|
Number.isFinite(primaryTimeForRebuild) &&
|
|
@@ -1215,26 +1007,8 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1215
1007
|
if (timesToLoad.length === 0 && !Number.isNaN(currentFrameTime)) {
|
|
1216
1008
|
timesToLoad = [currentFrameTime];
|
|
1217
1009
|
}
|
|
1218
|
-
this._debugLog('_rebuildLayerAndPreload:timeline', {
|
|
1219
|
-
rebuildId,
|
|
1220
|
-
isMRMS: state.isMRMS,
|
|
1221
|
-
variable: state.variable,
|
|
1222
|
-
normalizedLen: normalized.length,
|
|
1223
|
-
normalizedSummary: _debugSummarizeNumericSeries(normalized),
|
|
1224
|
-
currentFrameTime,
|
|
1225
|
-
timesToLoadLen: timesToLoad.length,
|
|
1226
|
-
timesToLoadSummary: _debugSummarizeNumericSeries(timesToLoad),
|
|
1227
|
-
insertBeforeId: beforeId ?? '(stack top)',
|
|
1228
|
-
});
|
|
1229
1010
|
if (timesToLoad.length === 0) {
|
|
1230
1011
|
this._initialGridLoadPending = false;
|
|
1231
|
-
this._debugLog('_rebuildLayerAndPreload:no times to load — check availableTimestamps / duration window', {
|
|
1232
|
-
rebuildId,
|
|
1233
|
-
availableTimestampsLen: Array.isArray(state.availableTimestamps)
|
|
1234
|
-
? state.availableTimestamps.length
|
|
1235
|
-
: 0,
|
|
1236
|
-
mrmsDurationValue: state.mrmsDurationValue,
|
|
1237
|
-
});
|
|
1238
1012
|
return;
|
|
1239
1013
|
}
|
|
1240
1014
|
|
|
@@ -1249,21 +1023,11 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1249
1023
|
const currentFrameTime = _rawPft == null ? NaN : Number(_rawPft);
|
|
1250
1024
|
const stepsToPreload = normalized.filter(t => t !== currentFrameTime);
|
|
1251
1025
|
|
|
1252
|
-
this._debugLog('_preloadAllTimeSteps', {
|
|
1253
|
-
normalizedLen: normalized.length,
|
|
1254
|
-
currentFrameTime,
|
|
1255
|
-
stepsToPreloadLen: stepsToPreload.length,
|
|
1256
|
-
stepsSummary: _debugSummarizeNumericSeries(stepsToPreload),
|
|
1257
|
-
});
|
|
1258
|
-
|
|
1259
1026
|
if (normalized.length === 0) {
|
|
1260
1027
|
return;
|
|
1261
1028
|
}
|
|
1262
1029
|
|
|
1263
1030
|
if (stepsToPreload.length === 0) {
|
|
1264
|
-
this._debugLog('_preloadAllTimeSteps:skip (nothing to preload besides current)', {
|
|
1265
|
-
currentFrameTime,
|
|
1266
|
-
});
|
|
1267
1031
|
return;
|
|
1268
1032
|
}
|
|
1269
1033
|
|
|
@@ -1510,7 +1274,6 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1510
1274
|
* Cleans up all resources.
|
|
1511
1275
|
*/
|
|
1512
1276
|
destroy() {
|
|
1513
|
-
this._debugLog('destroy');
|
|
1514
1277
|
this.setAutoRefresh(false);
|
|
1515
1278
|
|
|
1516
1279
|
// 1. Unbind the map's mousemove event listener
|