@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
|
@@ -251,10 +251,18 @@ public class GridRenderLayer implements CustomLayerHost {
|
|
|
251
251
|
floatMatrix[i] = matrix.get(i).floatValue();
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
floatMatrix[0] *= scale
|
|
255
|
-
floatMatrix[1] *= scale
|
|
256
|
-
floatMatrix[
|
|
257
|
-
floatMatrix[
|
|
254
|
+
floatMatrix[0] *= scale // X scale
|
|
255
|
+
floatMatrix[1] *= scale // X rotation
|
|
256
|
+
floatMatrix[2] *= scale // X Z-component
|
|
257
|
+
floatMatrix[3] *= scale // X translation
|
|
258
|
+
floatMatrix[4] *= scale // Y rotation
|
|
259
|
+
floatMatrix[5] *= scale // Y scale
|
|
260
|
+
floatMatrix[6] *= scale // Y Z-component
|
|
261
|
+
floatMatrix[7] *= scale // Y translation
|
|
262
|
+
floatMatrix[8] *= scale // Z X-component
|
|
263
|
+
floatMatrix[9] *= scale // Z Y-component
|
|
264
|
+
floatMatrix[10] *= scale // Z scale
|
|
265
|
+
floatMatrix[11] *= scale // Z translation
|
|
258
266
|
|
|
259
267
|
GLES20.glUniformMatrix4fv(uMatrix, 1, false, floatMatrix, 0);
|
|
260
268
|
GLES20.glUniform1f(uOpacity, opacity);
|
|
@@ -954,15 +954,23 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
954
954
|
// Apply zoom scale transformation
|
|
955
955
|
let zoom = parameters.zoom
|
|
956
956
|
let scale = Float(pow(2.0, zoom))
|
|
957
|
-
|
|
957
|
+
|
|
958
958
|
let matrixArray = parameters.projectionMatrix
|
|
959
959
|
var floatArray = matrixArray.map { $0.floatValue }
|
|
960
|
-
|
|
961
|
-
floatArray[0] *= scale
|
|
962
|
-
floatArray[1] *= scale
|
|
963
|
-
floatArray[
|
|
964
|
-
floatArray[
|
|
965
|
-
|
|
960
|
+
|
|
961
|
+
floatArray[0] *= scale // X scale
|
|
962
|
+
floatArray[1] *= scale // X rotation
|
|
963
|
+
floatArray[2] *= scale // X Z-component
|
|
964
|
+
floatArray[3] *= scale // X translation
|
|
965
|
+
floatArray[4] *= scale // Y rotation
|
|
966
|
+
floatArray[5] *= scale // Y scale
|
|
967
|
+
floatArray[6] *= scale // Y Z-component
|
|
968
|
+
floatArray[7] *= scale // Y translation
|
|
969
|
+
floatArray[8] *= scale // Z X-component
|
|
970
|
+
floatArray[9] *= scale // Z Y-component
|
|
971
|
+
floatArray[10] *= scale // Z scale
|
|
972
|
+
floatArray[11] *= scale // Z translation
|
|
973
|
+
|
|
966
974
|
let mvp = matrix_float4x4(
|
|
967
975
|
SIMD4<Float>(floatArray[0], floatArray[1], floatArray[2], floatArray[3]),
|
|
968
976
|
SIMD4<Float>(floatArray[4], floatArray[5], floatArray[6], floatArray[7]),
|
|
@@ -993,6 +1001,25 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
993
1001
|
encoder.endEncoding()
|
|
994
1002
|
}
|
|
995
1003
|
|
|
1004
|
+
deinit {
|
|
1005
|
+
print("🔴 [GridRenderLayer] deinit called for layer: \(id)")
|
|
1006
|
+
|
|
1007
|
+
// Clean up all resources
|
|
1008
|
+
frameCache.removeAll()
|
|
1009
|
+
vertexBuffer = nil
|
|
1010
|
+
indexBuffer = nil
|
|
1011
|
+
dataTexture = nil
|
|
1012
|
+
colormapTexture = nil
|
|
1013
|
+
device = nil
|
|
1014
|
+
commandQueue = nil
|
|
1015
|
+
pipelineState = nil
|
|
1016
|
+
dataSamplerState = nil
|
|
1017
|
+
colormapSamplerState = nil
|
|
1018
|
+
|
|
1019
|
+
// Break the reference cycle
|
|
1020
|
+
hostWrapper = nil
|
|
1021
|
+
}
|
|
1022
|
+
|
|
996
1023
|
internal func internalRenderingWillEnd() {
|
|
997
1024
|
print("🟢 [GridRenderLayer] renderingWillEnd called")
|
|
998
1025
|
vertexBuffer = nil
|
|
@@ -1001,3 +1028,16 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
1001
1028
|
colormapTexture = nil
|
|
1002
1029
|
}
|
|
1003
1030
|
}
|
|
1031
|
+
|
|
1032
|
+
extension GridRenderLayer {
|
|
1033
|
+
// Override to prevent KVC crashes during cleanup
|
|
1034
|
+
open override func value(forUndefinedKey key: String) -> Any? {
|
|
1035
|
+
print("⚠️ [GridRenderLayer] Attempted to access undefined key: \(key)")
|
|
1036
|
+
return nil
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
open override func setValue(_ value: Any?, forUndefinedKey key: String) {
|
|
1040
|
+
print("⚠️ [GridRenderLayer] Attempted to set undefined key: \(key)")
|
|
1041
|
+
// Silently ignore instead of crashing
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
@@ -239,4 +239,19 @@
|
|
|
239
239
|
[super removeFromSuperview];
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
- (void)dealloc {
|
|
243
|
+
RCTLogInfo(@"🔴 [GridRenderLayerView] dealloc called");
|
|
244
|
+
|
|
245
|
+
// Remove notification observer
|
|
246
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
247
|
+
|
|
248
|
+
// Clear the layer instance
|
|
249
|
+
if (_layerInstance) {
|
|
250
|
+
[_layerInstance clear];
|
|
251
|
+
_layerInstance = nil;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_mapView = nil;
|
|
255
|
+
}
|
|
256
|
+
|
|
242
257
|
@end
|
|
@@ -251,10 +251,18 @@ public class GridRenderLayer implements CustomLayerHost {
|
|
|
251
251
|
floatMatrix[i] = matrix.get(i).floatValue();
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
floatMatrix[0] *= scale
|
|
255
|
-
floatMatrix[1] *= scale
|
|
256
|
-
floatMatrix[
|
|
257
|
-
floatMatrix[
|
|
254
|
+
floatMatrix[0] *= scale // X scale
|
|
255
|
+
floatMatrix[1] *= scale // X rotation
|
|
256
|
+
floatMatrix[2] *= scale // X Z-component
|
|
257
|
+
floatMatrix[3] *= scale // X translation
|
|
258
|
+
floatMatrix[4] *= scale // Y rotation
|
|
259
|
+
floatMatrix[5] *= scale // Y scale
|
|
260
|
+
floatMatrix[6] *= scale // Y Z-component
|
|
261
|
+
floatMatrix[7] *= scale // Y translation
|
|
262
|
+
floatMatrix[8] *= scale // Z X-component
|
|
263
|
+
floatMatrix[9] *= scale // Z Y-component
|
|
264
|
+
floatMatrix[10] *= scale // Z scale
|
|
265
|
+
floatMatrix[11] *= scale // Z translation
|
|
258
266
|
|
|
259
267
|
GLES20.glUniformMatrix4fv(uMatrix, 1, false, floatMatrix, 0);
|
|
260
268
|
GLES20.glUniform1f(uOpacity, opacity);
|
|
@@ -954,15 +954,23 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
954
954
|
// Apply zoom scale transformation
|
|
955
955
|
let zoom = parameters.zoom
|
|
956
956
|
let scale = Float(pow(2.0, zoom))
|
|
957
|
-
|
|
957
|
+
|
|
958
958
|
let matrixArray = parameters.projectionMatrix
|
|
959
959
|
var floatArray = matrixArray.map { $0.floatValue }
|
|
960
|
-
|
|
961
|
-
floatArray[0] *= scale
|
|
962
|
-
floatArray[1] *= scale
|
|
963
|
-
floatArray[
|
|
964
|
-
floatArray[
|
|
965
|
-
|
|
960
|
+
|
|
961
|
+
floatArray[0] *= scale // X scale
|
|
962
|
+
floatArray[1] *= scale // X rotation
|
|
963
|
+
floatArray[2] *= scale // X Z-component
|
|
964
|
+
floatArray[3] *= scale // X translation
|
|
965
|
+
floatArray[4] *= scale // Y rotation
|
|
966
|
+
floatArray[5] *= scale // Y scale
|
|
967
|
+
floatArray[6] *= scale // Y Z-component
|
|
968
|
+
floatArray[7] *= scale // Y translation
|
|
969
|
+
floatArray[8] *= scale // Z X-component
|
|
970
|
+
floatArray[9] *= scale // Z Y-component
|
|
971
|
+
floatArray[10] *= scale // Z scale
|
|
972
|
+
floatArray[11] *= scale // Z translation
|
|
973
|
+
|
|
966
974
|
let mvp = matrix_float4x4(
|
|
967
975
|
SIMD4<Float>(floatArray[0], floatArray[1], floatArray[2], floatArray[3]),
|
|
968
976
|
SIMD4<Float>(floatArray[4], floatArray[5], floatArray[6], floatArray[7]),
|
|
@@ -993,6 +1001,25 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
993
1001
|
encoder.endEncoding()
|
|
994
1002
|
}
|
|
995
1003
|
|
|
1004
|
+
deinit {
|
|
1005
|
+
print("🔴 [GridRenderLayer] deinit called for layer: \(id)")
|
|
1006
|
+
|
|
1007
|
+
// Clean up all resources
|
|
1008
|
+
frameCache.removeAll()
|
|
1009
|
+
vertexBuffer = nil
|
|
1010
|
+
indexBuffer = nil
|
|
1011
|
+
dataTexture = nil
|
|
1012
|
+
colormapTexture = nil
|
|
1013
|
+
device = nil
|
|
1014
|
+
commandQueue = nil
|
|
1015
|
+
pipelineState = nil
|
|
1016
|
+
dataSamplerState = nil
|
|
1017
|
+
colormapSamplerState = nil
|
|
1018
|
+
|
|
1019
|
+
// Break the reference cycle
|
|
1020
|
+
hostWrapper = nil
|
|
1021
|
+
}
|
|
1022
|
+
|
|
996
1023
|
internal func internalRenderingWillEnd() {
|
|
997
1024
|
print("🟢 [GridRenderLayer] renderingWillEnd called")
|
|
998
1025
|
vertexBuffer = nil
|
|
@@ -1001,3 +1028,16 @@ internal func internalRenderingWillStart(_ metalDevice: MTLDevice, colorPixelFor
|
|
|
1001
1028
|
colormapTexture = nil
|
|
1002
1029
|
}
|
|
1003
1030
|
}
|
|
1031
|
+
|
|
1032
|
+
extension GridRenderLayer {
|
|
1033
|
+
// Override to prevent KVC crashes during cleanup
|
|
1034
|
+
open override func value(forUndefinedKey key: String) -> Any? {
|
|
1035
|
+
print("⚠️ [GridRenderLayer] Attempted to access undefined key: \(key)")
|
|
1036
|
+
return nil
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
open override func setValue(_ value: Any?, forUndefinedKey key: String) {
|
|
1040
|
+
print("⚠️ [GridRenderLayer] Attempted to set undefined key: \(key)")
|
|
1041
|
+
// Silently ignore instead of crashing
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
@@ -239,4 +239,19 @@
|
|
|
239
239
|
[super removeFromSuperview];
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
- (void)dealloc {
|
|
243
|
+
RCTLogInfo(@"🔴 [GridRenderLayerView] dealloc called");
|
|
244
|
+
|
|
245
|
+
// Remove notification observer
|
|
246
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
247
|
+
|
|
248
|
+
// Clear the layer instance
|
|
249
|
+
if (_layerInstance) {
|
|
250
|
+
[_layerInstance clear];
|
|
251
|
+
_layerInstance = nil;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_mapView = nil;
|
|
255
|
+
}
|
|
256
|
+
|
|
242
257
|
@end
|
|
@@ -14,12 +14,21 @@ var _MapRegistry = require("./MapRegistry");
|
|
|
14
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
15
|
// packages/react-native/src/WeatherLayerManager.js
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
function findLatestModelRun(modelsData, modelName) {
|
|
18
|
+
const model = modelsData?.[modelName];
|
|
19
|
+
if (!model) return null;
|
|
20
|
+
const availableDates = Object.keys(model).sort((a, b) => b.localeCompare(a));
|
|
21
|
+
for (const date of availableDates) {
|
|
22
|
+
const runs = model[date];
|
|
23
|
+
if (!runs) continue;
|
|
24
|
+
const availableRuns = Object.keys(runs).sort((a, b) => b.localeCompare(a));
|
|
25
|
+
if (availableRuns.length > 0) return {
|
|
26
|
+
date: date,
|
|
27
|
+
run: availableRuns[0]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
23
32
|
const {
|
|
24
33
|
WeatherFrameProcessorModule,
|
|
25
34
|
InspectorModule
|
|
@@ -71,7 +80,6 @@ _javascriptSdk.AguaceroCore.prototype.setMapCenter = function (center) {
|
|
|
71
80
|
this.emit('map:move', center);
|
|
72
81
|
};
|
|
73
82
|
const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
|
|
74
|
-
// EDIT: Destructure the new props
|
|
75
83
|
const {
|
|
76
84
|
inspectorEnabled,
|
|
77
85
|
onInspect,
|
|
@@ -79,6 +87,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
79
87
|
customColormaps,
|
|
80
88
|
initialMode,
|
|
81
89
|
initialVariable,
|
|
90
|
+
autoRefresh,
|
|
91
|
+
autoRefreshInterval,
|
|
82
92
|
...restProps
|
|
83
93
|
} = props;
|
|
84
94
|
const context = (0, _react.useContext)(_AguaceroContext.AguaceroContext);
|
|
@@ -95,13 +105,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
95
105
|
}), [apiKey]);
|
|
96
106
|
const gridLayerRef = (0, _react.useRef)(null);
|
|
97
107
|
const currentGridDataRef = (0, _react.useRef)(null);
|
|
108
|
+
const autoRefreshIntervalId = (0, _react.useRef)(null);
|
|
98
109
|
|
|
99
110
|
// Cache for preloaded grid data - stores the processed data ready for GPU upload
|
|
100
111
|
const preloadedDataCache = (0, _react.useRef)(new Map());
|
|
101
112
|
|
|
102
|
-
// Track what we're currently preloading to avoid duplicates
|
|
103
|
-
const preloadingSet = (0, _react.useRef)(new Set());
|
|
104
|
-
|
|
105
113
|
// Store geometry and colormap that don't change with forecast hour
|
|
106
114
|
const cachedGeometry = (0, _react.useRef)(null);
|
|
107
115
|
const cachedColormap = (0, _react.useRef)(null);
|
|
@@ -119,6 +127,18 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
119
127
|
dataRange: [0, 1]
|
|
120
128
|
});
|
|
121
129
|
(0, _react.useImperativeHandle)(ref, () => {
|
|
130
|
+
const setAutoRefresh = (enabled, intervalSeconds) => {
|
|
131
|
+
if (autoRefreshIntervalId.current) {
|
|
132
|
+
clearInterval(autoRefreshIntervalId.current);
|
|
133
|
+
autoRefreshIntervalId.current = null;
|
|
134
|
+
}
|
|
135
|
+
if (enabled) {
|
|
136
|
+
const effectiveInterval = (intervalSeconds || autoRefreshInterval || 30) * 1000;
|
|
137
|
+
// Run once immediately, then start the interval
|
|
138
|
+
_checkForUpdates();
|
|
139
|
+
autoRefreshIntervalId.current = setInterval(_checkForUpdates, effectiveInterval);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
122
142
|
return {
|
|
123
143
|
play: () => {
|
|
124
144
|
core.play();
|
|
@@ -147,9 +167,10 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
147
167
|
if (gridLayerRef.current) {
|
|
148
168
|
gridLayerRef.current.setSmoothing(enabled);
|
|
149
169
|
}
|
|
150
|
-
}
|
|
170
|
+
},
|
|
171
|
+
setAutoRefresh
|
|
151
172
|
};
|
|
152
|
-
}, [core]);
|
|
173
|
+
}, [core, autoRefreshInterval, _checkForUpdates]);
|
|
153
174
|
const preloadAllFramesToDisk = state => {
|
|
154
175
|
if (hasPreloadedRef.current) {
|
|
155
176
|
console.log('✅ [Preload] Gating preload; already initiated for this dataset.');
|
|
@@ -352,6 +373,22 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
352
373
|
});
|
|
353
374
|
});
|
|
354
375
|
};
|
|
376
|
+
(0, _react.useEffect)(() => {
|
|
377
|
+
// This effect manages the auto-refresh based on props
|
|
378
|
+
if (autoRefresh) {
|
|
379
|
+
const interval = (autoRefreshInterval || 30) * 1000;
|
|
380
|
+
_checkForUpdates(); // Run immediately on enable
|
|
381
|
+
autoRefreshIntervalId.current = setInterval(_checkForUpdates, interval);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Cleanup function: this runs when the component unmounts or props change
|
|
385
|
+
return () => {
|
|
386
|
+
if (autoRefreshIntervalId.current) {
|
|
387
|
+
clearInterval(autoRefreshIntervalId.current);
|
|
388
|
+
autoRefreshIntervalId.current = null;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}, [autoRefresh, autoRefreshInterval, _checkForUpdates]);
|
|
355
392
|
const updateGPUWithCachedData = state => {
|
|
356
393
|
const {
|
|
357
394
|
model,
|
|
@@ -554,12 +591,112 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
554
591
|
return null;
|
|
555
592
|
}
|
|
556
593
|
};
|
|
594
|
+
const _checkForUpdates = (0, _react.useMemo)(() => async () => {
|
|
595
|
+
if (!core) return;
|
|
596
|
+
const {
|
|
597
|
+
isMRMS,
|
|
598
|
+
model: currentModel,
|
|
599
|
+
variable: currentVariable,
|
|
600
|
+
date,
|
|
601
|
+
run
|
|
602
|
+
} = core.state;
|
|
603
|
+
if (isMRMS) {
|
|
604
|
+
// --- MRMS LOGIC WITH PRE-LOADING ---
|
|
605
|
+
const oldTimestamps = new Set(core.mrmsStatus?.[currentVariable] || []);
|
|
606
|
+
const mrmsStatus = await core.fetchMRMSStatus(true);
|
|
607
|
+
const newTimestamps = mrmsStatus?.[currentVariable] || [];
|
|
608
|
+
if (newTimestamps.length === 0) return;
|
|
609
|
+
const newTimestampsToPreload = newTimestamps.filter(ts => !oldTimestamps.has(ts));
|
|
610
|
+
if (newTimestampsToPreload.length > 0) {
|
|
611
|
+
core.mrmsStatus = mrmsStatus;
|
|
612
|
+
core._emitStateChange(); // Update UI slider without changing selection
|
|
613
|
+
|
|
614
|
+
console.log(`[Auto-Refresh] Preloading ${newTimestampsToPreload.length} new MRMS frames.`);
|
|
615
|
+
const {
|
|
616
|
+
corners,
|
|
617
|
+
gridDef
|
|
618
|
+
} = core._getGridCornersAndDef('mrms');
|
|
619
|
+
const {
|
|
620
|
+
nx,
|
|
621
|
+
ny
|
|
622
|
+
} = gridDef.grid_params;
|
|
623
|
+
newTimestampsToPreload.forEach(frame => {
|
|
624
|
+
const cacheKey = `mrms-${frame}-${currentVariable}`;
|
|
625
|
+
if (preloadedDataCache.current.has(cacheKey)) return;
|
|
626
|
+
const frameDate = new Date(frame * 1000);
|
|
627
|
+
const y = frameDate.getUTCFullYear(),
|
|
628
|
+
m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'),
|
|
629
|
+
d = frameDate.getUTCDate().toString().padStart(2, '0');
|
|
630
|
+
|
|
631
|
+
// --- THIS IS THE FIX ---
|
|
632
|
+
// Changed 'variable' to 'currentVariable'
|
|
633
|
+
const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
|
|
634
|
+
// --- END FIX ---
|
|
635
|
+
|
|
636
|
+
const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
|
|
637
|
+
WeatherFrameProcessorModule.processFrame({
|
|
638
|
+
url,
|
|
639
|
+
apiKey: core.apiKey,
|
|
640
|
+
bundleId: core.bundleId
|
|
641
|
+
}).then(result => {
|
|
642
|
+
if (!result || !result.filePath) return;
|
|
643
|
+
const frameData = {
|
|
644
|
+
filePath: result.filePath,
|
|
645
|
+
nx,
|
|
646
|
+
ny,
|
|
647
|
+
scale: result.scale,
|
|
648
|
+
offset: result.offset,
|
|
649
|
+
missing: result.missing,
|
|
650
|
+
corners,
|
|
651
|
+
gridDef,
|
|
652
|
+
scaleType: result.scaleType,
|
|
653
|
+
originalScale: result.scale,
|
|
654
|
+
originalOffset: result.offset
|
|
655
|
+
};
|
|
656
|
+
preloadedDataCache.current.set(cacheKey, frameData);
|
|
657
|
+
if (_reactNative.Platform.OS === 'ios' && gridLayerRef.current?.primeGpuCache) {
|
|
658
|
+
gridLayerRef.current.primeGpuCache({
|
|
659
|
+
[cacheKey]: frameData
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}).catch(error => console.warn(`[Auto-Refresh] Failed to preload frame ${frame}:`, error));
|
|
663
|
+
});
|
|
664
|
+
const newTimestampsSet = new Set(newTimestamps);
|
|
665
|
+
oldTimestamps.forEach(oldTs => {
|
|
666
|
+
if (!newTimestampsSet.has(oldTs)) {
|
|
667
|
+
const cacheKey = `mrms-${oldTs}-${currentVariable}`;
|
|
668
|
+
preloadedDataCache.current.delete(cacheKey);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
} else {
|
|
673
|
+
// --- MODEL LOGIC ---
|
|
674
|
+
const modelStatus = await core.fetchModelStatus(true);
|
|
675
|
+
const latestRun = findLatestModelRun(modelStatus, currentModel);
|
|
676
|
+
if (!latestRun) return;
|
|
677
|
+
const currentRunKey = `${date}:${run}`;
|
|
678
|
+
const latestRunKey = `${latestRun.date}:${latestRun.run}`;
|
|
679
|
+
if (latestRunKey > currentRunKey) {
|
|
680
|
+
// This preserves the current forecastHour while changing the run
|
|
681
|
+
await core.setState({
|
|
682
|
+
date: latestRun.date,
|
|
683
|
+
run: latestRun.run
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}, [core]);
|
|
557
688
|
(0, _react.useEffect)(() => {
|
|
558
689
|
if (!core) {
|
|
559
690
|
console.warn('⚠️ [useEffect] Core is not available yet');
|
|
560
691
|
return;
|
|
561
692
|
}
|
|
693
|
+
|
|
694
|
+
// --- REPLACE THIS ENTIRE FUNCTION ---
|
|
562
695
|
const handleStateChange = async newState => {
|
|
696
|
+
// --- FIX 1: Notify the parent component IMMEDIATELY ---
|
|
697
|
+
// This ensures the parent UI (like sliders) always gets the latest state,
|
|
698
|
+
// including updated lists of available hours or timestamps.
|
|
699
|
+
props.onStateChange?.(newState);
|
|
563
700
|
if (!previousStateRef.current) {
|
|
564
701
|
previousStateRef.current = core.state;
|
|
565
702
|
}
|
|
@@ -569,18 +706,19 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
569
706
|
}
|
|
570
707
|
const stateKey = `${newState.model}-${newState.variable}-${newState.date}-${newState.run}-${newState.forecastHour}-${newState.units}-${newState.mrmsTimestamp}`;
|
|
571
708
|
const isOpacityOnlyChange = hasInitialLoad.current && newState.opacity !== renderProps.opacity && newState.variable === previousStateRef.current?.variable && newState.forecastHour === previousStateRef.current?.forecastHour && newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp && newState.model === previousStateRef.current?.model && newState.units === previousStateRef.current?.units;
|
|
572
|
-
|
|
573
|
-
// ADD: Check for play state only change
|
|
574
709
|
const isPlayStateOnlyChange = hasInitialLoad.current && newState.isPlaying !== previousStateRef.current?.isPlaying && newState.variable === previousStateRef.current?.variable && newState.forecastHour === previousStateRef.current?.forecastHour && newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp && newState.model === previousStateRef.current?.model && newState.units === previousStateRef.current?.units && newState.opacity === previousStateRef.current?.opacity;
|
|
710
|
+
|
|
711
|
+
// This gate now correctly prevents only redundant *internal* processing.
|
|
712
|
+
// The parent has already been notified.
|
|
575
713
|
if (!isOpacityOnlyChange && !isPlayStateOnlyChange && lastProcessedState.current === stateKey) {
|
|
714
|
+
// --- FIX 2: Update the previous state ref before returning ---
|
|
715
|
+
// This is critical to prevent stale comparisons on the next state change event.
|
|
716
|
+
previousStateRef.current = newState;
|
|
576
717
|
return;
|
|
577
718
|
}
|
|
578
719
|
if (!isOpacityOnlyChange && !isPlayStateOnlyChange) {
|
|
579
720
|
lastProcessedState.current = stateKey;
|
|
580
721
|
}
|
|
581
|
-
|
|
582
|
-
// MOVE THIS TO THE TOP - Always notify the parent component of state changes
|
|
583
|
-
props.onStateChange?.(newState);
|
|
584
722
|
if (isOpacityOnlyChange) {
|
|
585
723
|
setRenderProps(prev => ({
|
|
586
724
|
...prev,
|
|
@@ -589,14 +727,10 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
589
727
|
previousStateRef.current = newState;
|
|
590
728
|
return;
|
|
591
729
|
}
|
|
592
|
-
|
|
593
|
-
// ADD: Handle play state only change
|
|
594
730
|
if (isPlayStateOnlyChange) {
|
|
595
731
|
previousStateRef.current = newState;
|
|
596
732
|
return;
|
|
597
733
|
}
|
|
598
|
-
|
|
599
|
-
// Check if only units changed
|
|
600
734
|
const isUnitsOnlyChange = hasInitialLoad.current && newState.model === previousStateRef.current.model && newState.isMRMS === previousStateRef.current.isMRMS && newState.variable === previousStateRef.current.variable && newState.date === previousStateRef.current.date && newState.run === previousStateRef.current.run && newState.forecastHour === previousStateRef.current.forecastHour && newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp && newState.units !== previousStateRef.current.units;
|
|
601
735
|
if (isUnitsOnlyChange) {
|
|
602
736
|
const {
|
|
@@ -620,18 +754,12 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
620
754
|
let dataOffset = cachedData.originalOffset;
|
|
621
755
|
if (baseUnit !== toUnit) {
|
|
622
756
|
const conversionFunc = (0, _javascriptSdk.getUnitConversionFunction)(baseUnit, toUnit);
|
|
623
|
-
console.log('🔧 [Unit Conversion] Conversion function exists:', !!conversionFunc);
|
|
624
757
|
if (conversionFunc) {
|
|
625
758
|
if (cachedData.scaleType === 'sqrt') {
|
|
626
|
-
// Calculate what the physical values would be at offset and offset+scale
|
|
627
759
|
const physicalAtOffset = dataOffset * dataOffset;
|
|
628
760
|
const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
|
|
629
|
-
|
|
630
|
-
// Convert these physical values to the new unit
|
|
631
761
|
const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
|
|
632
762
|
const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
|
|
633
|
-
|
|
634
|
-
// Take sqrt to get back to intermediate values
|
|
635
763
|
const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
|
|
636
764
|
const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
|
|
637
765
|
dataScale = newOffsetPlusScale - newOffset;
|
|
@@ -644,22 +772,11 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
644
772
|
}
|
|
645
773
|
}
|
|
646
774
|
}
|
|
647
|
-
|
|
648
|
-
// Update the colormap AND data range
|
|
649
775
|
const {
|
|
650
776
|
colormap
|
|
651
777
|
} = core._getColormapForVariable(variable);
|
|
652
778
|
const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
|
|
653
|
-
let dataRange;
|
|
654
|
-
if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
|
|
655
|
-
if (isMRMS) {
|
|
656
|
-
dataRange = [5, 380];
|
|
657
|
-
} else {
|
|
658
|
-
dataRange = [5, 380];
|
|
659
|
-
}
|
|
660
|
-
} else {
|
|
661
|
-
dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
|
|
662
|
-
}
|
|
779
|
+
let dataRange = variable === 'ptypeRefl' || variable === 'ptypeRate' ? [5, 380] : [finalColormap[0], finalColormap[finalColormap.length - 2]];
|
|
663
780
|
const colormapBytes = _generateColormapBytes(finalColormap);
|
|
664
781
|
const colormapAsBase64 = (0, _base64Js.fromByteArray)(colormapBytes);
|
|
665
782
|
gridLayerRef.current.updateColormapTexture(colormapAsBase64);
|
|
@@ -673,15 +790,8 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
673
790
|
opacity: newState.opacity
|
|
674
791
|
}));
|
|
675
792
|
if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
|
|
676
|
-
const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
|
|
677
|
-
|
|
678
|
-
console.log('🔄 [Unit Conversion] Calling updateDataParameters with:', {
|
|
679
|
-
scale: dataScale,
|
|
680
|
-
offset: dataOffset,
|
|
681
|
-
missing: cachedData.missing,
|
|
682
|
-
scaleType: scaleTypeValue
|
|
683
|
-
});
|
|
684
|
-
gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue); // Pass scaleType
|
|
793
|
+
const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
|
|
794
|
+
gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue);
|
|
685
795
|
}
|
|
686
796
|
const newCacheKey = isMRMS ? `mrms-${mrmsTimestamp}-${variable}` : `${model}-${date}-${run}-${forecastHour}-${variable}`;
|
|
687
797
|
preloadedDataCache.current.set(newCacheKey, {
|
|
@@ -689,8 +799,6 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
689
799
|
scale: dataScale,
|
|
690
800
|
offset: dataOffset
|
|
691
801
|
});
|
|
692
|
-
} else {
|
|
693
|
-
console.warn('⚠️ [Unit Conversion] No cached data found for key:', oldCacheKey);
|
|
694
802
|
}
|
|
695
803
|
previousStateRef.current = newState;
|
|
696
804
|
return;
|
|
@@ -700,7 +808,6 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
700
808
|
if (gridLayerRef.current) {
|
|
701
809
|
gridLayerRef.current.setVariable(newState.variable);
|
|
702
810
|
gridLayerRef.current.clear();
|
|
703
|
-
// --- OPTIMIZATION: Clear the native GPU cache on iOS ---
|
|
704
811
|
if (_reactNative.Platform.OS === 'ios' && gridLayerRef.current.clearGpuCache) {
|
|
705
812
|
gridLayerRef.current.clearGpuCache();
|
|
706
813
|
}
|
|
@@ -709,8 +816,6 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
709
816
|
preloadedDataCache.current.clear();
|
|
710
817
|
cachedGeometry.current = null;
|
|
711
818
|
cachedColormap.current = null;
|
|
712
|
-
|
|
713
|
-
// ADD THIS: Clear the inspector cache too
|
|
714
819
|
currentGridDataRef.current = null;
|
|
715
820
|
WeatherFrameProcessorModule.cancelAllFrames();
|
|
716
821
|
if (!newState.variable) {
|
|
@@ -720,24 +825,17 @@ const WeatherLayerManager = exports.WeatherLayerManager = /*#__PURE__*/(0, _reac
|
|
|
720
825
|
preloadAllFramesToDisk(newState);
|
|
721
826
|
} else if (newState.forecastHour !== previousStateRef.current.forecastHour || newState.isMRMS && newState.mrmsTimestamp !== previousStateRef.current.mrmsTimestamp) {
|
|
722
827
|
const success = updateGPUWithCachedData(newState);
|
|
723
|
-
if (
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
// The preload will eventually complete and trigger a re-render
|
|
729
|
-
} else {
|
|
730
|
-
// Only update inspector cache when we successfully loaded new data
|
|
731
|
-
if (newState.opacity !== renderProps.opacity) {
|
|
732
|
-
setRenderProps(prev => ({
|
|
733
|
-
...prev,
|
|
734
|
-
opacity: newState.opacity
|
|
735
|
-
}));
|
|
736
|
-
}
|
|
828
|
+
if (success && newState.opacity !== renderProps.opacity) {
|
|
829
|
+
setRenderProps(prev => ({
|
|
830
|
+
...prev,
|
|
831
|
+
opacity: newState.opacity
|
|
832
|
+
}));
|
|
737
833
|
}
|
|
738
834
|
}
|
|
739
835
|
previousStateRef.current = newState;
|
|
740
836
|
};
|
|
837
|
+
// --- END REPLACEMENT ---
|
|
838
|
+
|
|
741
839
|
handleStateChangeRef.current = handleStateChange;
|
|
742
840
|
const stableHandler = newState => {
|
|
743
841
|
const isOpacityOnlyChange = previousStateRef.current && newState.opacity !== previousStateRef.current.opacity && newState.variable === previousStateRef.current.variable && newState.forecastHour === previousStateRef.current.forecastHour && newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp && newState.model === previousStateRef.current.model && newState.units === previousStateRef.current.units && newState.date === previousStateRef.current.date && newState.run === previousStateRef.current.run;
|