@aguacerowx/mapsgl 0.0.42 → 0.0.44
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
CHANGED
|
@@ -308,6 +308,23 @@ export class NwsWatchesWarningsOverlay {
|
|
|
308
308
|
: timeMode.timelineUnix;
|
|
309
309
|
const useOnsetWindow = this._nwsAlertSettings.activeOnlyRealtime === true;
|
|
310
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Weather-timeline alignment (MRMS / satellite / NEXRAD scrubber): without a valid reference
|
|
313
|
+
* time we must not show every alert at full opacity — previously `isActive` defaulted to
|
|
314
|
+
* true for all features when `referenceUnix` was still null (race before first timestamp).
|
|
315
|
+
*/
|
|
316
|
+
if (!timeMode.useRealtime && (referenceUnix == null || !Number.isFinite(referenceUnix))) {
|
|
317
|
+
for (const f of this._workingFc.features ?? []) {
|
|
318
|
+
if (f.id == null) continue;
|
|
319
|
+
try {
|
|
320
|
+
m.setFeatureState({ source: this._sourceId, id: f.id }, { active: false });
|
|
321
|
+
} catch {
|
|
322
|
+
/* ignore */
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
311
328
|
for (const f of this._workingFc.features ?? []) {
|
|
312
329
|
if (f.id == null) continue;
|
|
313
330
|
let isActive = true;
|
|
@@ -8,6 +8,48 @@ import { NexradSitesOverlay } from './NexradSitesOverlay.js';
|
|
|
8
8
|
import { NwsWatchesWarningsOverlay } from './NwsWatchesWarningsOverlay.js';
|
|
9
9
|
import WorkerPool from './WorkerPool.js';
|
|
10
10
|
|
|
11
|
+
const DEBUG_NS = '[WeatherLayerManager:debug]';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Redact secrets for console output.
|
|
15
|
+
* @param {object} options
|
|
16
|
+
*/
|
|
17
|
+
function _debugSanitizeOptions(options) {
|
|
18
|
+
if (!options || typeof options !== 'object') return options;
|
|
19
|
+
const o = { ...options };
|
|
20
|
+
if (typeof o.apiKey === 'string' && o.apiKey.length > 0) {
|
|
21
|
+
o.apiKey = `${o.apiKey.slice(0, 4)}…(${o.apiKey.length} chars)`;
|
|
22
|
+
}
|
|
23
|
+
return o;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Compact summary of timestamp / hour arrays (MRMS timeline debugging).
|
|
28
|
+
* @param {unknown[]} arr
|
|
29
|
+
*/
|
|
30
|
+
function _debugSummarizeNumericSeries(arr) {
|
|
31
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
32
|
+
return { length: 0, min: null, max: null, spanSec: null, head: [], tail: undefined };
|
|
33
|
+
}
|
|
34
|
+
const nums = arr.map((x) => Number(x)).filter((n) => !Number.isNaN(n));
|
|
35
|
+
if (nums.length === 0) {
|
|
36
|
+
return { length: 0, min: null, max: null, spanSec: null, head: [], tail: undefined };
|
|
37
|
+
}
|
|
38
|
+
const sorted = [...nums].sort((a, b) => a - b);
|
|
39
|
+
const min = sorted[0];
|
|
40
|
+
const max = sorted[sorted.length - 1];
|
|
41
|
+
const head = sorted.slice(0, Math.min(8, sorted.length));
|
|
42
|
+
const tail = sorted.length > 16 ? sorted.slice(-8) : [];
|
|
43
|
+
return {
|
|
44
|
+
length: sorted.length,
|
|
45
|
+
min,
|
|
46
|
+
max,
|
|
47
|
+
spanSec: max != null && min != null ? max - min : null,
|
|
48
|
+
head,
|
|
49
|
+
tail: tail.length ? tail : undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
11
53
|
function findLatestModelRun(modelsData, modelName) {
|
|
12
54
|
const model = modelsData?.[modelName];
|
|
13
55
|
if (!model) return null;
|
|
@@ -33,12 +75,17 @@ function findLatestModelRun(modelsData, modelName) {
|
|
|
33
75
|
* @param {string} [options.belowID] - Style layer id to insert Aguacero weather layers **below** (default `AML_-_terrain` when present). Alias of `weatherBeforeLayerId`.
|
|
34
76
|
* @param {string} [options.weatherBeforeLayerId] - Same as `belowID`.
|
|
35
77
|
* @param {string} [options.nexradLayerId] - Override Mapbox id for the NEXRAD custom layer (default: derived from `layerId`).
|
|
78
|
+
* @param {boolean} [options.debug] - When `true`, logs detailed diagnostics to the console (prefix `[WeatherLayerManager:debug]`). Off by default.
|
|
36
79
|
*/
|
|
37
80
|
export class WeatherLayerManager extends EventEmitter {
|
|
38
81
|
constructor(map, options = {}) {
|
|
39
82
|
super();
|
|
40
83
|
if (!map) throw new Error('A Mapbox GL map instance is required.');
|
|
41
84
|
this.map = map;
|
|
85
|
+
/** @private When true, emit verbose `[WeatherLayerManager:debug]` logs. */
|
|
86
|
+
this._debug = options.debug === true;
|
|
87
|
+
/** @private Monotonic counter for correlating state:change logs. */
|
|
88
|
+
this._debugStateSeq = 0;
|
|
42
89
|
this.layerId =
|
|
43
90
|
options.layerId ||
|
|
44
91
|
options.id ||
|
|
@@ -109,6 +156,29 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
109
156
|
// 1. CREATE an instance of the core engine
|
|
110
157
|
this.core = new AguaceroCore(options);
|
|
111
158
|
|
|
159
|
+
if (this._debug) {
|
|
160
|
+
const layerOpts = options.layerOptions || {};
|
|
161
|
+
console.log(DEBUG_NS, 'constructor', {
|
|
162
|
+
layerId: this.layerId,
|
|
163
|
+
nexradLayerId: this._nexradLayerId,
|
|
164
|
+
mapLoaded: typeof this.map?.loaded === 'function' ? this.map.loaded() : undefined,
|
|
165
|
+
styleLoaded: this.map?.isStyleLoaded?.() ?? undefined,
|
|
166
|
+
weatherBeforeLayerId: this._weatherBeforeLayerId,
|
|
167
|
+
options: _debugSanitizeOptions(options),
|
|
168
|
+
layerOptions: layerOpts,
|
|
169
|
+
coreStateSnapshot: {
|
|
170
|
+
isMRMS: this.core.state?.isMRMS,
|
|
171
|
+
isSatellite: this.core.state?.isSatellite,
|
|
172
|
+
isNexrad: this.core.state?.isNexrad,
|
|
173
|
+
model: this.core.state?.model,
|
|
174
|
+
variable: this.core.state?.variable,
|
|
175
|
+
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
176
|
+
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
177
|
+
satelliteDurationValue: this.core.state?.satelliteDurationValue,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
112
182
|
// 2. LISTEN for events from the core engine
|
|
113
183
|
this.core.on('state:change', (newState) => {
|
|
114
184
|
this._lastEmittedState = newState;
|
|
@@ -128,6 +198,20 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
128
198
|
this.map.on('mousemove', this._handleMouseMove);
|
|
129
199
|
}
|
|
130
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Structured debug log (no-op unless `options.debug === true` on construction).
|
|
203
|
+
* @param {string} scope
|
|
204
|
+
* @param {object} [data]
|
|
205
|
+
*/
|
|
206
|
+
_debugLog(scope, data) {
|
|
207
|
+
if (!this._debug) return;
|
|
208
|
+
if (data !== undefined) {
|
|
209
|
+
console.log(DEBUG_NS, scope, data);
|
|
210
|
+
} else {
|
|
211
|
+
console.log(DEBUG_NS, scope);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
131
215
|
/**
|
|
132
216
|
* Resolves which style layer id to pass as Mapbox `addLayer(..., beforeId)` for satellite / grid / NEXRAD.
|
|
133
217
|
* Prefers an explicit id (custom styles), then the default Aguacero `AML_-_terrain` anchor when present.
|
|
@@ -174,9 +258,39 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
174
258
|
* @private
|
|
175
259
|
*/
|
|
176
260
|
_handleStateChange(state) {
|
|
261
|
+
const seq = this._debug ? ++this._debugStateSeq : 0;
|
|
177
262
|
/** 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. */
|
|
178
263
|
let deferNwsSyncUntilNexradReady = false;
|
|
179
264
|
try {
|
|
265
|
+
if (this._debug) {
|
|
266
|
+
const tsSummary = state.isMRMS
|
|
267
|
+
? _debugSummarizeNumericSeries(state.availableTimestamps || [])
|
|
268
|
+
: null;
|
|
269
|
+
this._debugLog('state:change', {
|
|
270
|
+
seq,
|
|
271
|
+
mode: state.isSatellite
|
|
272
|
+
? 'satellite'
|
|
273
|
+
: state.isNexrad
|
|
274
|
+
? 'nexrad'
|
|
275
|
+
: state.isMRMS
|
|
276
|
+
? 'mrms'
|
|
277
|
+
: 'model',
|
|
278
|
+
model: state.model,
|
|
279
|
+
variable: state.variable,
|
|
280
|
+
runKey: `${state.model}-${state.date}-${state.run}-${state.variable}`,
|
|
281
|
+
timeKey: state.isMRMS
|
|
282
|
+
? (state.mrmsTimestamp == null ? null : Number(state.mrmsTimestamp))
|
|
283
|
+
: Number(state.forecastHour),
|
|
284
|
+
mrmsDurationValue: state.mrmsDurationValue,
|
|
285
|
+
availableTimestampsSummary: tsSummary,
|
|
286
|
+
availableHoursLen: Array.isArray(state.availableHours) ? state.availableHours.length : 0,
|
|
287
|
+
shaderLayerRunKey: this.shaderLayer?.runKey ?? null,
|
|
288
|
+
currentLoadedTimeKey: this.currentLoadedTimeKey,
|
|
289
|
+
rebuildId: this.currentRebuildId,
|
|
290
|
+
initialGridLoadPending: this._initialGridLoadPending,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
180
294
|
if (state.isSatellite) {
|
|
181
295
|
this._handleSatelliteStateChange(state);
|
|
182
296
|
return;
|
|
@@ -206,6 +320,21 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
206
320
|
: Number(state.forecastHour);
|
|
207
321
|
const runKey = `${state.model}-${state.date}-${state.run}-${state.variable}`;
|
|
208
322
|
|
|
323
|
+
if (this._debug && state.isMRMS) {
|
|
324
|
+
this._debugLog('grid path (mrms/model)', {
|
|
325
|
+
seq,
|
|
326
|
+
prevMrmsDurationValue: prevMrmsDur,
|
|
327
|
+
mrmsDurationChanged,
|
|
328
|
+
willRebuild: !this.shaderLayer || this.shaderLayer.runKey !== runKey,
|
|
329
|
+
willUpdateData:
|
|
330
|
+
this.shaderLayer &&
|
|
331
|
+
this.shaderLayer.runKey === runKey &&
|
|
332
|
+
this.currentLoadedTimeKey !== timeKey,
|
|
333
|
+
shaderRunKey: this.shaderLayer?.runKey,
|
|
334
|
+
targetRunKey: runKey,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
209
338
|
if (!this.shaderLayer || this.shaderLayer.runKey !== runKey) {
|
|
210
339
|
this._rebuildLayerAndPreload(state);
|
|
211
340
|
} else if (this.currentLoadedTimeKey !== timeKey) {
|
|
@@ -215,6 +344,12 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
215
344
|
timeKey === this._rebuildTargetTimeKey;
|
|
216
345
|
if (!duplicateBeforeFirstPaint) {
|
|
217
346
|
this._updateLayerData(state);
|
|
347
|
+
} else if (this._debug) {
|
|
348
|
+
this._debugLog('skip _updateLayerData (duplicate before first paint)', {
|
|
349
|
+
seq,
|
|
350
|
+
timeKey,
|
|
351
|
+
_rebuildTargetTimeKey: this._rebuildTargetTimeKey,
|
|
352
|
+
});
|
|
218
353
|
}
|
|
219
354
|
}
|
|
220
355
|
|
|
@@ -225,6 +360,13 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
225
360
|
}
|
|
226
361
|
|
|
227
362
|
if (state.isMRMS && this.shaderLayer && mrmsDurationChanged) {
|
|
363
|
+
if (this._debug) {
|
|
364
|
+
this._debugLog('_preloadAllTimeSteps (mrms duration changed)', {
|
|
365
|
+
seq,
|
|
366
|
+
from: prevMrmsDur,
|
|
367
|
+
to: state.mrmsDurationValue,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
228
370
|
this._preloadAllTimeSteps(state);
|
|
229
371
|
}
|
|
230
372
|
} finally {
|
|
@@ -638,10 +780,68 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
638
780
|
// to the core engine. This keeps the API consistent for your users.
|
|
639
781
|
|
|
640
782
|
async initialize(options) {
|
|
783
|
+
const t0 =
|
|
784
|
+
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
785
|
+
? performance.now()
|
|
786
|
+
: Date.now();
|
|
787
|
+
this._debugLog('initialize:start', {
|
|
788
|
+
autoRefreshEnabled: this.autoRefreshEnabled,
|
|
789
|
+
autoRefreshIntervalSeconds: this.autoRefreshIntervalSeconds,
|
|
790
|
+
passedOptions: options && typeof options === 'object' ? { ...options } : options,
|
|
791
|
+
coreStateBefore: {
|
|
792
|
+
isMRMS: this.core.state?.isMRMS,
|
|
793
|
+
variable: this.core.state?.variable,
|
|
794
|
+
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
795
|
+
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
796
|
+
},
|
|
797
|
+
mrmsStatusVariableLen: this.core.state?.variable
|
|
798
|
+
? (this.core.mrmsStatus?.[this.core.state.variable]?.length ?? 'n/a')
|
|
799
|
+
: 'n/a',
|
|
800
|
+
});
|
|
641
801
|
if (this.autoRefreshEnabled) {
|
|
642
802
|
this.setAutoRefresh(true, this.autoRefreshIntervalSeconds);
|
|
643
803
|
}
|
|
644
|
-
|
|
804
|
+
try {
|
|
805
|
+
const result = await this.core.initialize({ ...options, autoRefresh: false });
|
|
806
|
+
const t1 =
|
|
807
|
+
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
808
|
+
? performance.now()
|
|
809
|
+
: Date.now();
|
|
810
|
+
this._debugLog('initialize:done', {
|
|
811
|
+
elapsedMs: Math.round(t1 - t0),
|
|
812
|
+
coreStateAfter: {
|
|
813
|
+
isMRMS: this.core.state?.isMRMS,
|
|
814
|
+
variable: this.core.state?.variable,
|
|
815
|
+
model: this.core.state?.model,
|
|
816
|
+
date: this.core.state?.date,
|
|
817
|
+
run: this.core.state?.run,
|
|
818
|
+
forecastHour: this.core.state?.forecastHour,
|
|
819
|
+
mrmsTimestamp: this.core.state?.mrmsTimestamp,
|
|
820
|
+
mrmsDurationValue: this.core.state?.mrmsDurationValue,
|
|
821
|
+
},
|
|
822
|
+
availableTimestampsSummary: _debugSummarizeNumericSeries(
|
|
823
|
+
this._lastEmittedState?.availableTimestamps || [],
|
|
824
|
+
),
|
|
825
|
+
availableHoursLen: Array.isArray(this._lastEmittedState?.availableHours)
|
|
826
|
+
? this._lastEmittedState.availableHours.length
|
|
827
|
+
: 0,
|
|
828
|
+
modelStatusLoaded: this.core.modelStatus != null,
|
|
829
|
+
mrmsStatusKeys:
|
|
830
|
+
this.core.mrmsStatus && typeof this.core.mrmsStatus === 'object'
|
|
831
|
+
? Object.keys(this.core.mrmsStatus).length
|
|
832
|
+
: 0,
|
|
833
|
+
mrmsStatusVariableLen: this.core.state?.variable
|
|
834
|
+
? (this.core.mrmsStatus?.[this.core.state.variable]?.length ?? 0)
|
|
835
|
+
: 0,
|
|
836
|
+
});
|
|
837
|
+
return result;
|
|
838
|
+
} catch (err) {
|
|
839
|
+
this._debugLog('initialize:error', {
|
|
840
|
+
message: err?.message || String(err),
|
|
841
|
+
stack: err?.stack,
|
|
842
|
+
});
|
|
843
|
+
throw err;
|
|
844
|
+
}
|
|
645
845
|
}
|
|
646
846
|
async setState(newState) { return this.core.setState(newState); }
|
|
647
847
|
play() { this.core.play(); }
|
|
@@ -741,15 +941,50 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
741
941
|
: (state.availableHours || []);
|
|
742
942
|
|
|
743
943
|
let timeSteps;
|
|
944
|
+
let mrmsTimelineSource = '';
|
|
744
945
|
if (state.isMRMS) {
|
|
745
|
-
|
|
946
|
+
if (fromState.length) {
|
|
947
|
+
timeSteps = fromState;
|
|
948
|
+
mrmsTimelineSource = 'state.availableTimestamps';
|
|
949
|
+
} else if (
|
|
950
|
+
state.variable &&
|
|
951
|
+
typeof this.core._getFilteredMrmsTimestampsForVariable === 'function'
|
|
952
|
+
) {
|
|
953
|
+
/**
|
|
954
|
+
* Same list {@link AguaceroCore} puts on `state:change` as `availableTimestamps`.
|
|
955
|
+
* When that array is missing or empty on this snapshot (ordering / first paint),
|
|
956
|
+
* derive from core so rebuild preload is not stuck with only `currentFrameTime`.
|
|
957
|
+
*/
|
|
958
|
+
try {
|
|
959
|
+
timeSteps = this.core._getFilteredMrmsTimestampsForVariable(state.variable);
|
|
960
|
+
mrmsTimelineSource = 'core._getFilteredMrmsTimestampsForVariable';
|
|
961
|
+
} catch {
|
|
962
|
+
timeSteps = fromCore;
|
|
963
|
+
mrmsTimelineSource = 'core forecast hours fallback (error)';
|
|
964
|
+
}
|
|
965
|
+
} else {
|
|
966
|
+
timeSteps = fromCore;
|
|
967
|
+
mrmsTimelineSource = 'empty';
|
|
968
|
+
}
|
|
746
969
|
} else {
|
|
747
970
|
timeSteps = fromCore.length > 0 ? fromCore : fromState;
|
|
748
971
|
}
|
|
749
972
|
|
|
750
|
-
|
|
973
|
+
const out = (timeSteps || [])
|
|
751
974
|
.map(t => Number(t))
|
|
752
975
|
.filter(t => !Number.isNaN(t));
|
|
976
|
+
if (this._debug && state.isMRMS) {
|
|
977
|
+
this._debugLog('_collectNormalizedTimelineSteps', {
|
|
978
|
+
variable: state.variable,
|
|
979
|
+
mrmsDurationValue: state.mrmsDurationValue,
|
|
980
|
+
fromStateLen: fromState.length,
|
|
981
|
+
fromCoreLen: fromCore.length,
|
|
982
|
+
chosenSource: mrmsTimelineSource || (state.isMRMS ? 'n/a' : 'model'),
|
|
983
|
+
normalizedLen: out.length,
|
|
984
|
+
summary: _debugSummarizeNumericSeries(out),
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
return out;
|
|
753
988
|
}
|
|
754
989
|
|
|
755
990
|
/**
|
|
@@ -763,6 +998,12 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
763
998
|
if (mode === 'rebuild') {
|
|
764
999
|
this._initialGridLoadPending = false;
|
|
765
1000
|
}
|
|
1001
|
+
this._debugLog('_runParallelGridFrameLoads:skip', {
|
|
1002
|
+
reason: !times.length ? 'no times' : 'no shaderLayer',
|
|
1003
|
+
mode,
|
|
1004
|
+
rebuildId,
|
|
1005
|
+
timesLen: times.length,
|
|
1006
|
+
});
|
|
766
1007
|
return;
|
|
767
1008
|
}
|
|
768
1009
|
|
|
@@ -778,6 +1019,25 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
778
1019
|
const gridModel = state.isMRMS ? 'mrms' : state.model;
|
|
779
1020
|
const { gridDef } = this.core._getGridCornersAndDef(gridModel);
|
|
780
1021
|
|
|
1022
|
+
this._debugLog('_runParallelGridFrameLoads', {
|
|
1023
|
+
mode,
|
|
1024
|
+
rebuildId,
|
|
1025
|
+
gridModel,
|
|
1026
|
+
timesLen: times.length,
|
|
1027
|
+
timesSummary: _debugSummarizeNumericSeries(times),
|
|
1028
|
+
currentFrameTime,
|
|
1029
|
+
primaryTimeForRebuild:
|
|
1030
|
+
mode === 'rebuild'
|
|
1031
|
+
? Number.isFinite(currentFrameTime)
|
|
1032
|
+
? currentFrameTime
|
|
1033
|
+
: times[0]
|
|
1034
|
+
: undefined,
|
|
1035
|
+
tsKey,
|
|
1036
|
+
gridNxNy: gridDef?.grid_params
|
|
1037
|
+
? { nx: gridDef.grid_params.nx, ny: gridDef.grid_params.ny }
|
|
1038
|
+
: null,
|
|
1039
|
+
});
|
|
1040
|
+
|
|
781
1041
|
times.forEach((time) => {
|
|
782
1042
|
const stateForTime = { ...state, [tsKey]: time };
|
|
783
1043
|
this.core._loadGridData(stateForTime)
|
|
@@ -798,9 +1058,20 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
798
1058
|
: Number(this.core.state.mrmsTimestamp))
|
|
799
1059
|
: Number(this.core.state.forecastHour);
|
|
800
1060
|
if (coreTimeKey !== this._rebuildTargetTimeKey) {
|
|
1061
|
+
this._debugLog('_loadGridData:skip primary (core time drifted vs rebuild target)', {
|
|
1062
|
+
time,
|
|
1063
|
+
coreTimeKey,
|
|
1064
|
+
_rebuildTargetTimeKey: this._rebuildTargetTimeKey,
|
|
1065
|
+
rebuildId,
|
|
1066
|
+
});
|
|
801
1067
|
return;
|
|
802
1068
|
}
|
|
803
1069
|
if (!grid?.data) {
|
|
1070
|
+
this._debugLog('_loadGridData:primary frame missing grid.data', {
|
|
1071
|
+
time,
|
|
1072
|
+
rebuildId,
|
|
1073
|
+
mode,
|
|
1074
|
+
});
|
|
804
1075
|
this._initialGridLoadPending = false;
|
|
805
1076
|
return;
|
|
806
1077
|
}
|
|
@@ -833,9 +1104,17 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
833
1104
|
this.currentLoadedTimeKey = time;
|
|
834
1105
|
this.map.triggerRepaint();
|
|
835
1106
|
}
|
|
1107
|
+
} else if (this._debug && mode === 'append') {
|
|
1108
|
+
this._debugLog('_loadGridData:empty grid (append)', { time, mode, rebuildId });
|
|
836
1109
|
}
|
|
837
1110
|
})
|
|
838
|
-
.catch(() => {
|
|
1111
|
+
.catch((err) => {
|
|
1112
|
+
this._debugLog('_loadGridData:error', {
|
|
1113
|
+
time,
|
|
1114
|
+
mode,
|
|
1115
|
+
rebuildId,
|
|
1116
|
+
message: err?.message || String(err),
|
|
1117
|
+
});
|
|
839
1118
|
if (
|
|
840
1119
|
mode === 'rebuild' &&
|
|
841
1120
|
Number.isFinite(primaryTimeForRebuild) &&
|
|
@@ -910,8 +1189,26 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
910
1189
|
if (timesToLoad.length === 0 && !Number.isNaN(currentFrameTime)) {
|
|
911
1190
|
timesToLoad = [currentFrameTime];
|
|
912
1191
|
}
|
|
1192
|
+
this._debugLog('_rebuildLayerAndPreload:timeline', {
|
|
1193
|
+
rebuildId,
|
|
1194
|
+
isMRMS: state.isMRMS,
|
|
1195
|
+
variable: state.variable,
|
|
1196
|
+
normalizedLen: normalized.length,
|
|
1197
|
+
normalizedSummary: _debugSummarizeNumericSeries(normalized),
|
|
1198
|
+
currentFrameTime,
|
|
1199
|
+
timesToLoadLen: timesToLoad.length,
|
|
1200
|
+
timesToLoadSummary: _debugSummarizeNumericSeries(timesToLoad),
|
|
1201
|
+
insertBeforeId: beforeId ?? '(stack top)',
|
|
1202
|
+
});
|
|
913
1203
|
if (timesToLoad.length === 0) {
|
|
914
1204
|
this._initialGridLoadPending = false;
|
|
1205
|
+
this._debugLog('_rebuildLayerAndPreload:no times to load — check availableTimestamps / duration window', {
|
|
1206
|
+
rebuildId,
|
|
1207
|
+
availableTimestampsLen: Array.isArray(state.availableTimestamps)
|
|
1208
|
+
? state.availableTimestamps.length
|
|
1209
|
+
: 0,
|
|
1210
|
+
mrmsDurationValue: state.mrmsDurationValue,
|
|
1211
|
+
});
|
|
915
1212
|
return;
|
|
916
1213
|
}
|
|
917
1214
|
|
|
@@ -925,11 +1222,21 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
925
1222
|
const currentFrameTime = Number(state.isMRMS ? state.mrmsTimestamp : state.forecastHour);
|
|
926
1223
|
const stepsToPreload = normalized.filter(t => t !== currentFrameTime);
|
|
927
1224
|
|
|
1225
|
+
this._debugLog('_preloadAllTimeSteps', {
|
|
1226
|
+
normalizedLen: normalized.length,
|
|
1227
|
+
currentFrameTime,
|
|
1228
|
+
stepsToPreloadLen: stepsToPreload.length,
|
|
1229
|
+
stepsSummary: _debugSummarizeNumericSeries(stepsToPreload),
|
|
1230
|
+
});
|
|
1231
|
+
|
|
928
1232
|
if (normalized.length === 0) {
|
|
929
1233
|
return;
|
|
930
1234
|
}
|
|
931
1235
|
|
|
932
1236
|
if (stepsToPreload.length === 0) {
|
|
1237
|
+
this._debugLog('_preloadAllTimeSteps:skip (nothing to preload besides current)', {
|
|
1238
|
+
currentFrameTime,
|
|
1239
|
+
});
|
|
933
1240
|
return;
|
|
934
1241
|
}
|
|
935
1242
|
|
|
@@ -1171,6 +1478,7 @@ export class WeatherLayerManager extends EventEmitter {
|
|
|
1171
1478
|
* Cleans up all resources.
|
|
1172
1479
|
*/
|
|
1173
1480
|
destroy() {
|
|
1481
|
+
this._debugLog('destroy');
|
|
1174
1482
|
this.setAutoRefresh(false);
|
|
1175
1483
|
|
|
1176
1484
|
// 1. Unbind the map's mousemove event listener
|