@aguacerowx/react-native 0.0.24 → 0.0.26
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/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +12 -4
- package/ios/GridRenderLayer.swift +47 -7
- package/ios/GridRenderLayerView.m +15 -0
- package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +12 -4
- package/lib/commonjs/ios/GridRenderLayer.swift +47 -7
- package/lib/commonjs/ios/GridRenderLayerView.m +15 -0
- package/lib/commonjs/package.json +1 -1
- package/lib/commonjs/src/WeatherLayerManager.js +165 -67
- package/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
- package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +12 -4
- package/lib/module/ios/GridRenderLayer.swift +47 -7
- package/lib/module/ios/GridRenderLayerView.m +15 -0
- package/lib/module/lib/commonjs/ios/GridRenderLayer.swift +47 -7
- package/lib/module/lib/commonjs/ios/GridRenderLayerView.m +15 -0
- package/lib/module/lib/commonjs/package.json +1 -1
- package/lib/module/lib/commonjs/src/WeatherLayerManager.js +165 -67
- package/lib/module/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
- package/lib/module/package.json +1 -1
- package/lib/module/src/WeatherLayerManager.js +165 -67
- package/lib/module/src/WeatherLayerManager.js.map +1 -1
- package/lib/typescript/src/WeatherLayerManager.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/WeatherLayerManager.js +147 -83
|
@@ -8,13 +8,18 @@ import { fromByteArray } from 'base64-js';
|
|
|
8
8
|
import { Platform, NativeModules } from 'react-native';
|
|
9
9
|
import { mapRegistry } from './MapRegistry';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
function findLatestModelRun(modelsData, modelName) {
|
|
12
|
+
const model = modelsData?.[modelName];
|
|
13
|
+
if (!model) return null;
|
|
14
|
+
const availableDates = Object.keys(model).sort((a, b) => b.localeCompare(a));
|
|
15
|
+
for (const date of availableDates) {
|
|
16
|
+
const runs = model[date];
|
|
17
|
+
if (!runs) continue;
|
|
18
|
+
const availableRuns = Object.keys(runs).sort((a, b) => b.localeCompare(a));
|
|
19
|
+
if (availableRuns.length > 0) return { date: date, run: availableRuns[0] };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
18
23
|
const { WeatherFrameProcessorModule, InspectorModule } = NativeModules;
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -67,8 +72,17 @@ AguaceroCore.prototype.setMapCenter = function(center) {
|
|
|
67
72
|
};
|
|
68
73
|
|
|
69
74
|
export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
const {
|
|
76
|
+
inspectorEnabled,
|
|
77
|
+
onInspect,
|
|
78
|
+
apiKey,
|
|
79
|
+
customColormaps,
|
|
80
|
+
initialMode,
|
|
81
|
+
initialVariable,
|
|
82
|
+
autoRefresh,
|
|
83
|
+
autoRefreshInterval,
|
|
84
|
+
...restProps
|
|
85
|
+
} = props;
|
|
72
86
|
const context = useContext(AguaceroContext);
|
|
73
87
|
|
|
74
88
|
// Create the core here instead of getting it from context
|
|
@@ -84,13 +98,11 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
84
98
|
|
|
85
99
|
const gridLayerRef = useRef(null);
|
|
86
100
|
const currentGridDataRef = useRef(null);
|
|
101
|
+
const autoRefreshIntervalId = useRef(null);
|
|
87
102
|
|
|
88
103
|
// Cache for preloaded grid data - stores the processed data ready for GPU upload
|
|
89
104
|
const preloadedDataCache = useRef(new Map());
|
|
90
105
|
|
|
91
|
-
// Track what we're currently preloading to avoid duplicates
|
|
92
|
-
const preloadingSet = useRef(new Set());
|
|
93
|
-
|
|
94
106
|
// Store geometry and colormap that don't change with forecast hour
|
|
95
107
|
const cachedGeometry = useRef(null);
|
|
96
108
|
const cachedColormap = useRef(null);
|
|
@@ -110,6 +122,18 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
110
122
|
});
|
|
111
123
|
|
|
112
124
|
useImperativeHandle(ref, () => {
|
|
125
|
+
const setAutoRefresh = (enabled, intervalSeconds) => {
|
|
126
|
+
if (autoRefreshIntervalId.current) {
|
|
127
|
+
clearInterval(autoRefreshIntervalId.current);
|
|
128
|
+
autoRefreshIntervalId.current = null;
|
|
129
|
+
}
|
|
130
|
+
if (enabled) {
|
|
131
|
+
const effectiveInterval = (intervalSeconds || autoRefreshInterval || 30) * 1000;
|
|
132
|
+
// Run once immediately, then start the interval
|
|
133
|
+
_checkForUpdates();
|
|
134
|
+
autoRefreshIntervalId.current = setInterval(_checkForUpdates, effectiveInterval);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
113
137
|
return {
|
|
114
138
|
play: () => {
|
|
115
139
|
core.play();
|
|
@@ -137,8 +161,9 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
137
161
|
gridLayerRef.current.setSmoothing(enabled);
|
|
138
162
|
}
|
|
139
163
|
},
|
|
164
|
+
setAutoRefresh,
|
|
140
165
|
};
|
|
141
|
-
}, [core]);
|
|
166
|
+
}, [core, autoRefreshInterval, _checkForUpdates]);
|
|
142
167
|
|
|
143
168
|
const preloadAllFramesToDisk = (state) => {
|
|
144
169
|
|
|
@@ -337,6 +362,23 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
337
362
|
});
|
|
338
363
|
};
|
|
339
364
|
|
|
365
|
+
useEffect(() => {
|
|
366
|
+
// This effect manages the auto-refresh based on props
|
|
367
|
+
if (autoRefresh) {
|
|
368
|
+
const interval = (autoRefreshInterval || 30) * 1000;
|
|
369
|
+
_checkForUpdates(); // Run immediately on enable
|
|
370
|
+
autoRefreshIntervalId.current = setInterval(_checkForUpdates, interval);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Cleanup function: this runs when the component unmounts or props change
|
|
374
|
+
return () => {
|
|
375
|
+
if (autoRefreshIntervalId.current) {
|
|
376
|
+
clearInterval(autoRefreshIntervalId.current);
|
|
377
|
+
autoRefreshIntervalId.current = null;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}, [autoRefresh, autoRefreshInterval, _checkForUpdates]);
|
|
381
|
+
|
|
340
382
|
const updateGPUWithCachedData = (state) => {
|
|
341
383
|
const { model, date, run, forecastHour, variable, units, isMRMS, mrmsTimestamp } = state;
|
|
342
384
|
|
|
@@ -547,13 +589,92 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
547
589
|
}
|
|
548
590
|
};
|
|
549
591
|
|
|
592
|
+
const _checkForUpdates = useMemo(() => async () => {
|
|
593
|
+
if (!core) return;
|
|
594
|
+
const { isMRMS, model: currentModel, variable: currentVariable, date, run } = core.state;
|
|
595
|
+
|
|
596
|
+
if (isMRMS) {
|
|
597
|
+
// --- MRMS LOGIC WITH PRE-LOADING ---
|
|
598
|
+
const oldTimestamps = new Set(core.mrmsStatus?.[currentVariable] || []);
|
|
599
|
+
const mrmsStatus = await core.fetchMRMSStatus(true);
|
|
600
|
+
const newTimestamps = mrmsStatus?.[currentVariable] || [];
|
|
601
|
+
if (newTimestamps.length === 0) return;
|
|
602
|
+
|
|
603
|
+
const newTimestampsToPreload = newTimestamps.filter(ts => !oldTimestamps.has(ts));
|
|
604
|
+
|
|
605
|
+
if (newTimestampsToPreload.length > 0) {
|
|
606
|
+
core.mrmsStatus = mrmsStatus;
|
|
607
|
+
core._emitStateChange(); // Update UI slider without changing selection
|
|
608
|
+
|
|
609
|
+
console.log(`[Auto-Refresh] Preloading ${newTimestampsToPreload.length} new MRMS frames.`);
|
|
610
|
+
const { corners, gridDef } = core._getGridCornersAndDef('mrms');
|
|
611
|
+
const { nx, ny } = gridDef.grid_params;
|
|
612
|
+
|
|
613
|
+
newTimestampsToPreload.forEach(frame => {
|
|
614
|
+
const cacheKey = `mrms-${frame}-${currentVariable}`;
|
|
615
|
+
if (preloadedDataCache.current.has(cacheKey)) return;
|
|
616
|
+
|
|
617
|
+
const frameDate = new Date(frame * 1000);
|
|
618
|
+
const y = frameDate.getUTCFullYear(), m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'), d = frameDate.getUTCDate().toString().padStart(2, '0');
|
|
619
|
+
|
|
620
|
+
// --- THIS IS THE FIX ---
|
|
621
|
+
// Changed 'variable' to 'currentVariable'
|
|
622
|
+
const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
|
|
623
|
+
// --- END FIX ---
|
|
624
|
+
|
|
625
|
+
const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
|
|
626
|
+
|
|
627
|
+
WeatherFrameProcessorModule.processFrame({ url, apiKey: core.apiKey, bundleId: core.bundleId })
|
|
628
|
+
.then(result => {
|
|
629
|
+
if (!result || !result.filePath) return;
|
|
630
|
+
const frameData = { filePath: result.filePath, nx, ny, scale: result.scale, offset: result.offset, missing: result.missing, corners, gridDef, scaleType: result.scaleType, originalScale: result.scale, originalOffset: result.offset };
|
|
631
|
+
preloadedDataCache.current.set(cacheKey, frameData);
|
|
632
|
+
if (Platform.OS === 'ios' && gridLayerRef.current?.primeGpuCache) {
|
|
633
|
+
gridLayerRef.current.primeGpuCache({ [cacheKey]: frameData });
|
|
634
|
+
}
|
|
635
|
+
}).catch(error => console.warn(`[Auto-Refresh] Failed to preload frame ${frame}:`, error));
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
const newTimestampsSet = new Set(newTimestamps);
|
|
639
|
+
oldTimestamps.forEach(oldTs => {
|
|
640
|
+
if (!newTimestampsSet.has(oldTs)) {
|
|
641
|
+
const cacheKey = `mrms-${oldTs}-${currentVariable}`;
|
|
642
|
+
preloadedDataCache.current.delete(cacheKey);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
// --- MODEL LOGIC ---
|
|
648
|
+
const modelStatus = await core.fetchModelStatus(true);
|
|
649
|
+
const latestRun = findLatestModelRun(modelStatus, currentModel);
|
|
650
|
+
if (!latestRun) return;
|
|
651
|
+
|
|
652
|
+
const currentRunKey = `${date}:${run}`;
|
|
653
|
+
const latestRunKey = `${latestRun.date}:${latestRun.run}`;
|
|
654
|
+
|
|
655
|
+
if (latestRunKey > currentRunKey) {
|
|
656
|
+
// This preserves the current forecastHour while changing the run
|
|
657
|
+
await core.setState({
|
|
658
|
+
date: latestRun.date,
|
|
659
|
+
run: latestRun.run,
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}, [core]);
|
|
664
|
+
|
|
550
665
|
useEffect(() => {
|
|
551
666
|
if (!core) {
|
|
552
667
|
console.warn('⚠️ [useEffect] Core is not available yet');
|
|
553
668
|
return;
|
|
554
669
|
}
|
|
555
670
|
|
|
671
|
+
// --- REPLACE THIS ENTIRE FUNCTION ---
|
|
556
672
|
const handleStateChange = async (newState) => {
|
|
673
|
+
// --- FIX 1: Notify the parent component IMMEDIATELY ---
|
|
674
|
+
// This ensures the parent UI (like sliders) always gets the latest state,
|
|
675
|
+
// including updated lists of available hours or timestamps.
|
|
676
|
+
props.onStateChange?.(newState);
|
|
677
|
+
|
|
557
678
|
if (!previousStateRef.current) {
|
|
558
679
|
previousStateRef.current = core.state;
|
|
559
680
|
}
|
|
@@ -575,7 +696,6 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
575
696
|
newState.model === previousStateRef.current?.model &&
|
|
576
697
|
newState.units === previousStateRef.current?.units;
|
|
577
698
|
|
|
578
|
-
// ADD: Check for play state only change
|
|
579
699
|
const isPlayStateOnlyChange =
|
|
580
700
|
hasInitialLoad.current &&
|
|
581
701
|
newState.isPlaying !== previousStateRef.current?.isPlaying &&
|
|
@@ -586,7 +706,12 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
586
706
|
newState.units === previousStateRef.current?.units &&
|
|
587
707
|
newState.opacity === previousStateRef.current?.opacity;
|
|
588
708
|
|
|
709
|
+
// This gate now correctly prevents only redundant *internal* processing.
|
|
710
|
+
// The parent has already been notified.
|
|
589
711
|
if (!isOpacityOnlyChange && !isPlayStateOnlyChange && lastProcessedState.current === stateKey) {
|
|
712
|
+
// --- FIX 2: Update the previous state ref before returning ---
|
|
713
|
+
// This is critical to prevent stale comparisons on the next state change event.
|
|
714
|
+
previousStateRef.current = newState;
|
|
590
715
|
return;
|
|
591
716
|
}
|
|
592
717
|
|
|
@@ -594,22 +719,17 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
594
719
|
lastProcessedState.current = stateKey;
|
|
595
720
|
}
|
|
596
721
|
|
|
597
|
-
// MOVE THIS TO THE TOP - Always notify the parent component of state changes
|
|
598
|
-
props.onStateChange?.(newState);
|
|
599
|
-
|
|
600
722
|
if (isOpacityOnlyChange) {
|
|
601
723
|
setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
|
|
602
724
|
previousStateRef.current = newState;
|
|
603
725
|
return;
|
|
604
726
|
}
|
|
605
727
|
|
|
606
|
-
// ADD: Handle play state only change
|
|
607
728
|
if (isPlayStateOnlyChange) {
|
|
608
729
|
previousStateRef.current = newState;
|
|
609
730
|
return;
|
|
610
731
|
}
|
|
611
732
|
|
|
612
|
-
// Check if only units changed
|
|
613
733
|
const isUnitsOnlyChange =
|
|
614
734
|
hasInitialLoad.current &&
|
|
615
735
|
newState.model === previousStateRef.current.model &&
|
|
@@ -632,88 +752,48 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
632
752
|
if (cachedData && cachedData.originalScale !== undefined && cachedData.originalOffset !== undefined) {
|
|
633
753
|
const { baseUnit } = core._getColormapForVariable(variable);
|
|
634
754
|
const toUnit = core._getTargetUnit(baseUnit, units);
|
|
635
|
-
|
|
636
755
|
let dataScale = cachedData.originalScale;
|
|
637
756
|
let dataOffset = cachedData.originalOffset;
|
|
638
757
|
|
|
639
758
|
if (baseUnit !== toUnit) {
|
|
640
759
|
const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
|
|
641
|
-
console.log('🔧 [Unit Conversion] Conversion function exists:', !!conversionFunc);
|
|
642
|
-
|
|
643
760
|
if (conversionFunc) {
|
|
644
761
|
if (cachedData.scaleType === 'sqrt') {
|
|
645
|
-
// Calculate what the physical values would be at offset and offset+scale
|
|
646
762
|
const physicalAtOffset = dataOffset * dataOffset;
|
|
647
763
|
const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
|
|
648
|
-
|
|
649
|
-
// Convert these physical values to the new unit
|
|
650
764
|
const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
|
|
651
765
|
const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
|
|
652
|
-
|
|
653
|
-
// Take sqrt to get back to intermediate values
|
|
654
766
|
const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
|
|
655
767
|
const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
|
|
656
|
-
|
|
657
768
|
dataScale = newOffsetPlusScale - newOffset;
|
|
658
769
|
dataOffset = newOffset;
|
|
659
770
|
} else {
|
|
660
771
|
const convertedOffset = conversionFunc(dataOffset);
|
|
661
772
|
const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
|
|
662
|
-
|
|
663
773
|
dataScale = convertedOffsetPlusScale - convertedOffset;
|
|
664
774
|
dataOffset = convertedOffset;
|
|
665
775
|
}
|
|
666
776
|
}
|
|
667
777
|
}
|
|
668
778
|
|
|
669
|
-
// Update the colormap AND data range
|
|
670
779
|
const { colormap } = core._getColormapForVariable(variable);
|
|
671
780
|
const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
|
|
672
|
-
let dataRange;
|
|
673
|
-
if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
|
|
674
|
-
if (isMRMS) {
|
|
675
|
-
dataRange = [5, 380];
|
|
676
|
-
} else {
|
|
677
|
-
dataRange = [5, 380];
|
|
678
|
-
}
|
|
679
|
-
} else {
|
|
680
|
-
dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
|
|
681
|
-
}
|
|
682
|
-
|
|
781
|
+
let dataRange = (variable === 'ptypeRefl' || variable === 'ptypeRate') ? [5, 380] : [finalColormap[0], finalColormap[finalColormap.length - 2]];
|
|
683
782
|
const colormapBytes = _generateColormapBytes(finalColormap);
|
|
684
783
|
const colormapAsBase64 = fromByteArray(colormapBytes);
|
|
685
784
|
|
|
686
785
|
gridLayerRef.current.updateColormapTexture(colormapAsBase64);
|
|
687
786
|
cachedColormap.current = { key: `${variable}-${units}` };
|
|
688
787
|
cachedDataRange.current = dataRange;
|
|
689
|
-
|
|
690
788
|
setRenderProps(prev => ({ ...prev, dataRange, opacity: newState.opacity }));
|
|
691
789
|
|
|
692
790
|
if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
|
|
693
|
-
const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
|
|
694
|
-
|
|
695
|
-
console.log('🔄 [Unit Conversion] Calling updateDataParameters with:', {
|
|
696
|
-
scale: dataScale,
|
|
697
|
-
offset: dataOffset,
|
|
698
|
-
missing: cachedData.missing,
|
|
699
|
-
scaleType: scaleTypeValue
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue); // Pass scaleType
|
|
791
|
+
const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
|
|
792
|
+
gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue);
|
|
703
793
|
}
|
|
704
794
|
|
|
705
|
-
const newCacheKey = isMRMS
|
|
706
|
-
|
|
707
|
-
: `${model}-${date}-${run}-${forecastHour}-${variable}`;
|
|
708
|
-
|
|
709
|
-
preloadedDataCache.current.set(newCacheKey, {
|
|
710
|
-
...cachedData,
|
|
711
|
-
scale: dataScale,
|
|
712
|
-
offset: dataOffset
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
} else {
|
|
716
|
-
console.warn('⚠️ [Unit Conversion] No cached data found for key:', oldCacheKey);
|
|
795
|
+
const newCacheKey = isMRMS ? `mrms-${mrmsTimestamp}-${variable}` : `${model}-${date}-${run}-${forecastHour}-${variable}`;
|
|
796
|
+
preloadedDataCache.current.set(newCacheKey, { ...cachedData, scale: dataScale, offset: dataOffset });
|
|
717
797
|
}
|
|
718
798
|
|
|
719
799
|
previousStateRef.current = newState;
|
|
@@ -729,51 +809,35 @@ export const WeatherLayerManager = forwardRef((props, ref) => {
|
|
|
729
809
|
newState.run !== previousStateRef.current.run;
|
|
730
810
|
|
|
731
811
|
if (needsFullLoad) {
|
|
732
|
-
|
|
733
812
|
if (gridLayerRef.current) {
|
|
734
813
|
gridLayerRef.current.setVariable(newState.variable);
|
|
735
814
|
gridLayerRef.current.clear();
|
|
736
|
-
// --- OPTIMIZATION: Clear the native GPU cache on iOS ---
|
|
737
815
|
if (Platform.OS === 'ios' && gridLayerRef.current.clearGpuCache) {
|
|
738
816
|
gridLayerRef.current.clearGpuCache();
|
|
739
817
|
}
|
|
740
818
|
}
|
|
741
819
|
hasPreloadedRef.current = false;
|
|
742
|
-
|
|
743
820
|
preloadedDataCache.current.clear();
|
|
744
821
|
cachedGeometry.current = null;
|
|
745
822
|
cachedColormap.current = null;
|
|
746
|
-
|
|
747
|
-
// ADD THIS: Clear the inspector cache too
|
|
748
823
|
currentGridDataRef.current = null;
|
|
749
|
-
|
|
750
824
|
WeatherFrameProcessorModule.cancelAllFrames();
|
|
751
825
|
|
|
752
826
|
if (!newState.variable) {
|
|
753
827
|
previousStateRef.current = newState;
|
|
754
828
|
return;
|
|
755
829
|
}
|
|
756
|
-
|
|
757
830
|
preloadAllFramesToDisk(newState);
|
|
758
831
|
} else if (newState.forecastHour !== previousStateRef.current.forecastHour || (newState.isMRMS && newState.mrmsTimestamp !== previousStateRef.current.mrmsTimestamp)) {
|
|
759
832
|
const success = updateGPUWithCachedData(newState);
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
// CHANGED: Don't error, just log and wait for preload
|
|
763
|
-
const timeKey = newState.isMRMS ? `timestamp ${newState.mrmsTimestamp}` : `hour +${newState.forecastHour}`;
|
|
764
|
-
console.log(`⏳ [handleStateChange] Frame ${timeKey} not ready yet, waiting for preload...`);
|
|
765
|
-
// Don't clear the layer - keep showing the previous frame until new one is ready
|
|
766
|
-
// The preload will eventually complete and trigger a re-render
|
|
767
|
-
} else {
|
|
768
|
-
// Only update inspector cache when we successfully loaded new data
|
|
769
|
-
if (newState.opacity !== renderProps.opacity) {
|
|
770
|
-
setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
|
|
771
|
-
}
|
|
833
|
+
if (success && newState.opacity !== renderProps.opacity) {
|
|
834
|
+
setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
|
|
772
835
|
}
|
|
773
836
|
}
|
|
774
837
|
|
|
775
838
|
previousStateRef.current = newState;
|
|
776
839
|
};
|
|
840
|
+
// --- END REPLACEMENT ---
|
|
777
841
|
|
|
778
842
|
handleStateChangeRef.current = handleStateChange;
|
|
779
843
|
|