@aguacerowx/react-native 0.0.51 → 0.0.52
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/SatelliteLayerView.java +11 -3
- package/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +315 -311
- package/ios/SatelliteLayerView.swift +11 -4
- package/ios/WeatherFrameProcessorModule.swift +222 -219
- package/lib/commonjs/WeatherLayerManager.js +61 -45
- package/lib/commonjs/WeatherLayerManager.js.map +1 -1
- package/lib/commonjs/aguaceroRnDebug.js +8 -1
- package/lib/commonjs/aguaceroRnDebug.js.map +1 -1
- package/lib/commonjs/gridCdnAuth.js +64 -0
- package/lib/commonjs/gridCdnAuth.js.map +1 -0
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/nexrad/nexradAndroidController.js +25 -25
- package/lib/commonjs/nexrad/nexradAndroidController.js.map +1 -1
- package/lib/commonjs/nexrad/nexradDiag.js +24 -24
- package/lib/commonjs/satellite/satelliteAndroidController.js +15 -15
- package/lib/module/WeatherLayerManager.js +61 -45
- package/lib/module/WeatherLayerManager.js.map +1 -1
- package/lib/module/aguaceroRnDebug.js +8 -1
- package/lib/module/aguaceroRnDebug.js.map +1 -1
- package/lib/module/gridCdnAuth.js +56 -0
- package/lib/module/gridCdnAuth.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/nexrad/nexradAndroidController.js +25 -25
- package/lib/module/nexrad/nexradAndroidController.js.map +1 -1
- package/lib/module/nexrad/nexradDiag.js +24 -24
- package/lib/module/satellite/satelliteAndroidController.js +15 -15
- package/lib/typescript/WeatherLayerManager.d.ts.map +1 -1
- package/lib/typescript/aguaceroRnDebug.d.ts.map +1 -1
- package/lib/typescript/gridCdnAuth.d.ts +24 -0
- package/lib/typescript/gridCdnAuth.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/WeatherLayerManager.js +2024 -2004
- package/src/aguaceroRnDebug.js +8 -1
- package/src/gridCdnAuth.js +56 -0
- package/src/index.js +19 -15
- package/src/nexrad/nexradAndroidController.js +1078 -1078
- package/src/nexrad/nexradDiag.js +150 -150
- package/src/satellite/satelliteAndroidController.js +245 -245
|
@@ -1,245 +1,245 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AguaceroCore satellite mode → Android native {@link SatelliteLayerView} (parity with mapsgl
|
|
3
|
-
* {@link WeatherLayerManager} satellite paths — preload timeline + scrub via {@code targetUnix}).
|
|
4
|
-
*/
|
|
5
|
-
import { resolveSatelliteS3FileName } from '@aguacerowx/javascript-sdk';
|
|
6
|
-
import { satBridgeWarn } from '../satelliteBridgeDiag';
|
|
7
|
-
import { aguaceroDebug, getAguaceroAuthDiagnosticSnapshot, isAguaceroRnDebugEnabled, redactApiKeyFromUrl } from '../aguaceroRnDebug';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Target frame first, then remaining frames by increasing temporal distance to {@code targetUnix}.
|
|
11
|
-
* @param {{ unix: number; url: string; shaderFileName: string }[]} frames
|
|
12
|
-
* @param {number | null} targetUnix
|
|
13
|
-
*/
|
|
14
|
-
export function sortFramesByTargetProximity(frames, targetUnix) {
|
|
15
|
-
if (!frames.length) return frames;
|
|
16
|
-
if (targetUnix == null || !Number.isFinite(targetUnix)) {
|
|
17
|
-
return frames;
|
|
18
|
-
}
|
|
19
|
-
const withDistance = frames.map((f) => ({
|
|
20
|
-
...f,
|
|
21
|
-
_d: Math.abs(f.unix - targetUnix),
|
|
22
|
-
}));
|
|
23
|
-
withDistance.sort((a, b) => {
|
|
24
|
-
if (a._d !== b._d) return a._d - b._d;
|
|
25
|
-
return a.unix - b.unix;
|
|
26
|
-
});
|
|
27
|
-
return withDistance.map(({ _d, ...rest }) => rest);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @param {import('@aguacerowx/javascript-sdk').AguaceroCoreState} state
|
|
32
|
-
* @param {number} satelliteTimestamp
|
|
33
|
-
*/
|
|
34
|
-
export function buildSatelliteFetchParts(state, satelliteTimestamp) {
|
|
35
|
-
const fileName = resolveSatelliteS3FileName(
|
|
36
|
-
state.satelliteChannel,
|
|
37
|
-
state.satelliteTimeToFileMap,
|
|
38
|
-
satelliteTimestamp,
|
|
39
|
-
);
|
|
40
|
-
if (!fileName) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
const channelName = state.satelliteChannel || state.variable;
|
|
44
|
-
const channelNameUpper = String(channelName).toUpperCase();
|
|
45
|
-
let s3FileName = fileName.replace('MULTI', channelNameUpper);
|
|
46
|
-
if (!s3FileName.endsWith('.ktx2')) {
|
|
47
|
-
s3FileName += '.ktx2';
|
|
48
|
-
}
|
|
49
|
-
let shaderFileName = s3FileName;
|
|
50
|
-
if (!channelNameUpper.startsWith('C') || channelNameUpper.length > 3) {
|
|
51
|
-
const token = channelNameUpper.toLowerCase().replace(/_/g, '');
|
|
52
|
-
shaderFileName = `${s3FileName}_${token}_`;
|
|
53
|
-
}
|
|
54
|
-
const apiKey = state.apiKey;
|
|
55
|
-
if (!apiKey) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const url = `https://d3dc62msmxkrd7.cloudfront.net/satellite/${s3FileName}?userId=${encodeURIComponent('sdk-user')}&apiKey=${encodeURIComponent(apiKey)}`;
|
|
59
|
-
return { url, shaderFileName, frameKey: Number(satelliteTimestamp), apiKey };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export class SatelliteAndroidController {
|
|
63
|
-
/**
|
|
64
|
-
* @param {import('@aguacerowx/javascript-sdk').AguaceroCore} core
|
|
65
|
-
* @param {React.RefObject<{ syncSatellite?: (json: string) => void; clearSatellite?: () => void; activateSatelliteCachedUnix?: (unix: number) => void; updateSatelliteStyle?: (json: string) => void }>} satelliteLayerRef
|
|
66
|
-
*/
|
|
67
|
-
constructor(core, satelliteLayerRef) {
|
|
68
|
-
this.core = core;
|
|
69
|
-
this.satelliteLayerRef = satelliteLayerRef;
|
|
70
|
-
this._destroyed = false;
|
|
71
|
-
/** @type {string | undefined} */
|
|
72
|
-
this._cachedRunKey = undefined;
|
|
73
|
-
/** @type {string | undefined} */
|
|
74
|
-
this._cachedTimelineSig = undefined;
|
|
75
|
-
/** @type {number | null | undefined} */
|
|
76
|
-
this._lastTargetUnix = undefined;
|
|
77
|
-
/** @type {string | undefined} */
|
|
78
|
-
this._lastStyleJson = undefined;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
destroy() {
|
|
82
|
-
this._destroyed = true;
|
|
83
|
-
this._cachedRunKey = undefined;
|
|
84
|
-
this._cachedTimelineSig = undefined;
|
|
85
|
-
this._lastTargetUnix = undefined;
|
|
86
|
-
this._lastStyleJson = undefined;
|
|
87
|
-
const native = this.satelliteLayerRef?.current;
|
|
88
|
-
native?.clearSatellite?.();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* @param {import('@aguacerowx/javascript-sdk').AguaceroCoreState} state
|
|
93
|
-
*/
|
|
94
|
-
sync(state) {
|
|
95
|
-
satBridgeWarn('SatelliteAndroidController.sync enter', {
|
|
96
|
-
destroyed: this._destroyed,
|
|
97
|
-
isSatellite: Boolean(state?.isSatellite),
|
|
98
|
-
instrumentId: state?.satelliteInstrumentId ?? null,
|
|
99
|
-
sector: state?.satelliteSectorLabel ?? null,
|
|
100
|
-
channel: state?.satelliteChannel ?? null,
|
|
101
|
-
tlMapSize:
|
|
102
|
-
state?.satelliteTimeToFileMap && typeof state.satelliteTimeToFileMap === 'object'
|
|
103
|
-
? Object.keys(state.satelliteTimeToFileMap).length
|
|
104
|
-
: 0,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
if (this._destroyed || !state?.isSatellite) {
|
|
108
|
-
satBridgeWarn('SatelliteAndroidController.sync early-exit', {
|
|
109
|
-
destroyed: this._destroyed,
|
|
110
|
-
isSatellite: state?.isSatellite,
|
|
111
|
-
});
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const native = this.satelliteLayerRef?.current;
|
|
116
|
-
if (!native?.syncSatellite) {
|
|
117
|
-
satBridgeWarn('SatelliteAndroidController.sync abort: no native.syncSatellite', {
|
|
118
|
-
refExists: Boolean(this.satelliteLayerRef?.current),
|
|
119
|
-
keys: this.satelliteLayerRef?.current ? Object.keys(this.satelliteLayerRef.current) : [],
|
|
120
|
-
});
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const keys = Object.keys(state.satelliteTimeToFileMap || {}).sort((a, b) => Number(a) - Number(b));
|
|
125
|
-
const satRunKey = `${state.satelliteInstrumentId}|${state.satelliteSectorLabel}|${state.satelliteChannel}|${state.variable || ''}`;
|
|
126
|
-
const timelineSig = keys.join(',');
|
|
127
|
-
|
|
128
|
-
const mergedState = {
|
|
129
|
-
...state,
|
|
130
|
-
apiKey: state.apiKey || this.core.apiKey,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const frames = [];
|
|
134
|
-
for (const k of keys) {
|
|
135
|
-
const ts = Number(k);
|
|
136
|
-
if (!Number.isFinite(ts)) continue;
|
|
137
|
-
const parts = buildSatelliteFetchParts(mergedState, ts);
|
|
138
|
-
if (!parts) continue;
|
|
139
|
-
frames.push({
|
|
140
|
-
unix: ts,
|
|
141
|
-
url: parts.url,
|
|
142
|
-
shaderFileName: parts.shaderFileName,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (frames.length === 0) {
|
|
147
|
-
console.warn('[AguaceroWX][satellite][satelliteSyncNoFrames]', {
|
|
148
|
-
satRunKey,
|
|
149
|
-
timelineKeyCount: keys.length,
|
|
150
|
-
hasApiKey: Boolean(mergedState.apiKey),
|
|
151
|
-
satelliteChannel: mergedState.satelliteChannel,
|
|
152
|
-
mapKeys:
|
|
153
|
-
mergedState.satelliteTimeToFileMap &&
|
|
154
|
-
typeof mergedState.satelliteTimeToFileMap === 'object'
|
|
155
|
-
? Object.keys(mergedState.satelliteTimeToFileMap).length
|
|
156
|
-
: 0,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let targetUnix =
|
|
161
|
-
state.satelliteTimestamp == null || state.satelliteTimestamp === undefined
|
|
162
|
-
? null
|
|
163
|
-
: Number(state.satelliteTimestamp);
|
|
164
|
-
|
|
165
|
-
// Core often omits satelliteTimestamp on the first satellite emit; without targetUnix the native
|
|
166
|
-
// layer never promotes uploads to activeUnix until a later activateSatelliteCachedUnix — blank map.
|
|
167
|
-
if (targetUnix == null || !Number.isFinite(targetUnix)) {
|
|
168
|
-
const nk = keys.map((k) => Number(k)).filter((n) => Number.isFinite(n));
|
|
169
|
-
if (nk.length > 0) {
|
|
170
|
-
targetUnix = Math.max(...nk);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const stylePayload = {
|
|
175
|
-
visible: state.visible !== false,
|
|
176
|
-
opacity: state.opacity ?? 1,
|
|
177
|
-
fillSmoothing: 0,
|
|
178
|
-
};
|
|
179
|
-
const styleJson = JSON.stringify(stylePayload);
|
|
180
|
-
|
|
181
|
-
const runKeyChanged = satRunKey !== this._cachedRunKey;
|
|
182
|
-
const timelineChanged = timelineSig !== this._cachedTimelineSig;
|
|
183
|
-
|
|
184
|
-
if (runKeyChanged || timelineChanged) {
|
|
185
|
-
if (isAguaceroRnDebugEnabled()) {
|
|
186
|
-
aguaceroDebug('satellite.sync.payload', {
|
|
187
|
-
auth: getAguaceroAuthDiagnosticSnapshot(this.core),
|
|
188
|
-
frameCount: frames.length,
|
|
189
|
-
sampleFrameUrl: frames[0] ? redactApiKeyFromUrl(frames[0].url) : null,
|
|
190
|
-
gridRequestSiteOrigin: mergedState.gridRequestSiteOrigin ?? this.core.gridRequestSiteOrigin ?? null,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
const sortedFrames = sortFramesByTargetProximity(frames, targetUnix);
|
|
194
|
-
const payload = {
|
|
195
|
-
runKey: satRunKey,
|
|
196
|
-
// Must match query params on each frame URL (same merge as buildSatelliteFetchParts) and mapsgl fetch headers.
|
|
197
|
-
apiKey: mergedState.apiKey || '',
|
|
198
|
-
userId: 'sdk-user',
|
|
199
|
-
// RN has no browser default Origin; native satellite fetch must mirror WeatherFrameProcessor / AguaceroCore grid headers.
|
|
200
|
-
gridRequestSiteOrigin:
|
|
201
|
-
typeof this.core.gridRequestSiteOrigin === 'string' ? this.core.gridRequestSiteOrigin : '',
|
|
202
|
-
bundleId: this.core.bundleId || '',
|
|
203
|
-
visible: stylePayload.visible,
|
|
204
|
-
opacity: stylePayload.opacity,
|
|
205
|
-
fillSmoothing: stylePayload.fillSmoothing,
|
|
206
|
-
frames: sortedFrames,
|
|
207
|
-
};
|
|
208
|
-
if (targetUnix != null && Number.isFinite(targetUnix)) {
|
|
209
|
-
payload.targetUnix = targetUnix;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const payloadJson = JSON.stringify(payload);
|
|
213
|
-
native.syncSatellite(payloadJson);
|
|
214
|
-
satBridgeWarn('SatelliteAndroidController.sync full payload sent', {
|
|
215
|
-
runKey: satRunKey,
|
|
216
|
-
timelineChanged,
|
|
217
|
-
frameCount: sortedFrames.length,
|
|
218
|
-
targetUnix: payload.targetUnix ?? null,
|
|
219
|
-
jsonChars: payloadJson.length,
|
|
220
|
-
});
|
|
221
|
-
this._cachedRunKey = satRunKey;
|
|
222
|
-
this._cachedTimelineSig = timelineSig;
|
|
223
|
-
this._lastTargetUnix = targetUnix != null && Number.isFinite(targetUnix) ? targetUnix : null;
|
|
224
|
-
this._lastStyleJson = styleJson;
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Same timeline: avoid large JSON over the bridge — scrub + style deltas only.
|
|
229
|
-
const finiteTarget =
|
|
230
|
-
targetUnix != null && Number.isFinite(targetUnix) ? targetUnix : null;
|
|
231
|
-
if (finiteTarget !== this._lastTargetUnix) {
|
|
232
|
-
if (finiteTarget != null && native.activateSatelliteCachedUnix) {
|
|
233
|
-
native.activateSatelliteCachedUnix(finiteTarget);
|
|
234
|
-
satBridgeWarn('SatelliteAndroidController.activateSatelliteCachedUnix', { unix: finiteTarget });
|
|
235
|
-
}
|
|
236
|
-
this._lastTargetUnix = finiteTarget;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (styleJson !== this._lastStyleJson && native.updateSatelliteStyle) {
|
|
240
|
-
native.updateSatelliteStyle(styleJson);
|
|
241
|
-
satBridgeWarn('SatelliteAndroidController.updateSatelliteStyle', { jsonChars: styleJson.length });
|
|
242
|
-
this._lastStyleJson = styleJson;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AguaceroCore satellite mode → Android native {@link SatelliteLayerView} (parity with mapsgl
|
|
3
|
+
* {@link WeatherLayerManager} satellite paths — preload timeline + scrub via {@code targetUnix}).
|
|
4
|
+
*/
|
|
5
|
+
import { resolveSatelliteS3FileName } from '@aguacerowx/javascript-sdk';
|
|
6
|
+
import { satBridgeWarn } from '../satelliteBridgeDiag';
|
|
7
|
+
import { aguaceroDebug, getAguaceroAuthDiagnosticSnapshot, isAguaceroRnDebugEnabled, redactApiKeyFromUrl } from '../aguaceroRnDebug';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Target frame first, then remaining frames by increasing temporal distance to {@code targetUnix}.
|
|
11
|
+
* @param {{ unix: number; url: string; shaderFileName: string }[]} frames
|
|
12
|
+
* @param {number | null} targetUnix
|
|
13
|
+
*/
|
|
14
|
+
export function sortFramesByTargetProximity(frames, targetUnix) {
|
|
15
|
+
if (!frames.length) return frames;
|
|
16
|
+
if (targetUnix == null || !Number.isFinite(targetUnix)) {
|
|
17
|
+
return frames;
|
|
18
|
+
}
|
|
19
|
+
const withDistance = frames.map((f) => ({
|
|
20
|
+
...f,
|
|
21
|
+
_d: Math.abs(f.unix - targetUnix),
|
|
22
|
+
}));
|
|
23
|
+
withDistance.sort((a, b) => {
|
|
24
|
+
if (a._d !== b._d) return a._d - b._d;
|
|
25
|
+
return a.unix - b.unix;
|
|
26
|
+
});
|
|
27
|
+
return withDistance.map(({ _d, ...rest }) => rest);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {import('@aguacerowx/javascript-sdk').AguaceroCoreState} state
|
|
32
|
+
* @param {number} satelliteTimestamp
|
|
33
|
+
*/
|
|
34
|
+
export function buildSatelliteFetchParts(state, satelliteTimestamp) {
|
|
35
|
+
const fileName = resolveSatelliteS3FileName(
|
|
36
|
+
state.satelliteChannel,
|
|
37
|
+
state.satelliteTimeToFileMap,
|
|
38
|
+
satelliteTimestamp,
|
|
39
|
+
);
|
|
40
|
+
if (!fileName) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const channelName = state.satelliteChannel || state.variable;
|
|
44
|
+
const channelNameUpper = String(channelName).toUpperCase();
|
|
45
|
+
let s3FileName = fileName.replace('MULTI', channelNameUpper);
|
|
46
|
+
if (!s3FileName.endsWith('.ktx2')) {
|
|
47
|
+
s3FileName += '.ktx2';
|
|
48
|
+
}
|
|
49
|
+
let shaderFileName = s3FileName;
|
|
50
|
+
if (!channelNameUpper.startsWith('C') || channelNameUpper.length > 3) {
|
|
51
|
+
const token = channelNameUpper.toLowerCase().replace(/_/g, '');
|
|
52
|
+
shaderFileName = `${s3FileName}_${token}_`;
|
|
53
|
+
}
|
|
54
|
+
const apiKey = state.apiKey;
|
|
55
|
+
if (!apiKey) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const url = `https://d3dc62msmxkrd7.cloudfront.net/satellite/${s3FileName}?userId=${encodeURIComponent('sdk-user')}&apiKey=${encodeURIComponent(apiKey)}`;
|
|
59
|
+
return { url, shaderFileName, frameKey: Number(satelliteTimestamp), apiKey };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class SatelliteAndroidController {
|
|
63
|
+
/**
|
|
64
|
+
* @param {import('@aguacerowx/javascript-sdk').AguaceroCore} core
|
|
65
|
+
* @param {React.RefObject<{ syncSatellite?: (json: string) => void; clearSatellite?: () => void; activateSatelliteCachedUnix?: (unix: number) => void; updateSatelliteStyle?: (json: string) => void }>} satelliteLayerRef
|
|
66
|
+
*/
|
|
67
|
+
constructor(core, satelliteLayerRef) {
|
|
68
|
+
this.core = core;
|
|
69
|
+
this.satelliteLayerRef = satelliteLayerRef;
|
|
70
|
+
this._destroyed = false;
|
|
71
|
+
/** @type {string | undefined} */
|
|
72
|
+
this._cachedRunKey = undefined;
|
|
73
|
+
/** @type {string | undefined} */
|
|
74
|
+
this._cachedTimelineSig = undefined;
|
|
75
|
+
/** @type {number | null | undefined} */
|
|
76
|
+
this._lastTargetUnix = undefined;
|
|
77
|
+
/** @type {string | undefined} */
|
|
78
|
+
this._lastStyleJson = undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
destroy() {
|
|
82
|
+
this._destroyed = true;
|
|
83
|
+
this._cachedRunKey = undefined;
|
|
84
|
+
this._cachedTimelineSig = undefined;
|
|
85
|
+
this._lastTargetUnix = undefined;
|
|
86
|
+
this._lastStyleJson = undefined;
|
|
87
|
+
const native = this.satelliteLayerRef?.current;
|
|
88
|
+
native?.clearSatellite?.();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {import('@aguacerowx/javascript-sdk').AguaceroCoreState} state
|
|
93
|
+
*/
|
|
94
|
+
sync(state) {
|
|
95
|
+
satBridgeWarn('SatelliteAndroidController.sync enter', {
|
|
96
|
+
destroyed: this._destroyed,
|
|
97
|
+
isSatellite: Boolean(state?.isSatellite),
|
|
98
|
+
instrumentId: state?.satelliteInstrumentId ?? null,
|
|
99
|
+
sector: state?.satelliteSectorLabel ?? null,
|
|
100
|
+
channel: state?.satelliteChannel ?? null,
|
|
101
|
+
tlMapSize:
|
|
102
|
+
state?.satelliteTimeToFileMap && typeof state.satelliteTimeToFileMap === 'object'
|
|
103
|
+
? Object.keys(state.satelliteTimeToFileMap).length
|
|
104
|
+
: 0,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (this._destroyed || !state?.isSatellite) {
|
|
108
|
+
satBridgeWarn('SatelliteAndroidController.sync early-exit', {
|
|
109
|
+
destroyed: this._destroyed,
|
|
110
|
+
isSatellite: state?.isSatellite,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const native = this.satelliteLayerRef?.current;
|
|
116
|
+
if (!native?.syncSatellite) {
|
|
117
|
+
satBridgeWarn('SatelliteAndroidController.sync abort: no native.syncSatellite', {
|
|
118
|
+
refExists: Boolean(this.satelliteLayerRef?.current),
|
|
119
|
+
keys: this.satelliteLayerRef?.current ? Object.keys(this.satelliteLayerRef.current) : [],
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const keys = Object.keys(state.satelliteTimeToFileMap || {}).sort((a, b) => Number(a) - Number(b));
|
|
125
|
+
const satRunKey = `${state.satelliteInstrumentId}|${state.satelliteSectorLabel}|${state.satelliteChannel}|${state.variable || ''}`;
|
|
126
|
+
const timelineSig = keys.join(',');
|
|
127
|
+
|
|
128
|
+
const mergedState = {
|
|
129
|
+
...state,
|
|
130
|
+
apiKey: state.apiKey || this.core.apiKey,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const frames = [];
|
|
134
|
+
for (const k of keys) {
|
|
135
|
+
const ts = Number(k);
|
|
136
|
+
if (!Number.isFinite(ts)) continue;
|
|
137
|
+
const parts = buildSatelliteFetchParts(mergedState, ts);
|
|
138
|
+
if (!parts) continue;
|
|
139
|
+
frames.push({
|
|
140
|
+
unix: ts,
|
|
141
|
+
url: parts.url,
|
|
142
|
+
shaderFileName: parts.shaderFileName,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (frames.length === 0) {
|
|
147
|
+
console.warn('[AguaceroWX][satellite][satelliteSyncNoFrames]', {
|
|
148
|
+
satRunKey,
|
|
149
|
+
timelineKeyCount: keys.length,
|
|
150
|
+
hasApiKey: Boolean(mergedState.apiKey),
|
|
151
|
+
satelliteChannel: mergedState.satelliteChannel,
|
|
152
|
+
mapKeys:
|
|
153
|
+
mergedState.satelliteTimeToFileMap &&
|
|
154
|
+
typeof mergedState.satelliteTimeToFileMap === 'object'
|
|
155
|
+
? Object.keys(mergedState.satelliteTimeToFileMap).length
|
|
156
|
+
: 0,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let targetUnix =
|
|
161
|
+
state.satelliteTimestamp == null || state.satelliteTimestamp === undefined
|
|
162
|
+
? null
|
|
163
|
+
: Number(state.satelliteTimestamp);
|
|
164
|
+
|
|
165
|
+
// Core often omits satelliteTimestamp on the first satellite emit; without targetUnix the native
|
|
166
|
+
// layer never promotes uploads to activeUnix until a later activateSatelliteCachedUnix — blank map.
|
|
167
|
+
if (targetUnix == null || !Number.isFinite(targetUnix)) {
|
|
168
|
+
const nk = keys.map((k) => Number(k)).filter((n) => Number.isFinite(n));
|
|
169
|
+
if (nk.length > 0) {
|
|
170
|
+
targetUnix = Math.max(...nk);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const stylePayload = {
|
|
175
|
+
visible: state.visible !== false,
|
|
176
|
+
opacity: state.opacity ?? 1,
|
|
177
|
+
fillSmoothing: 0,
|
|
178
|
+
};
|
|
179
|
+
const styleJson = JSON.stringify(stylePayload);
|
|
180
|
+
|
|
181
|
+
const runKeyChanged = satRunKey !== this._cachedRunKey;
|
|
182
|
+
const timelineChanged = timelineSig !== this._cachedTimelineSig;
|
|
183
|
+
|
|
184
|
+
if (runKeyChanged || timelineChanged) {
|
|
185
|
+
if (isAguaceroRnDebugEnabled()) {
|
|
186
|
+
aguaceroDebug('satellite.sync.payload', {
|
|
187
|
+
auth: getAguaceroAuthDiagnosticSnapshot(this.core),
|
|
188
|
+
frameCount: frames.length,
|
|
189
|
+
sampleFrameUrl: frames[0] ? redactApiKeyFromUrl(frames[0].url) : null,
|
|
190
|
+
gridRequestSiteOrigin: mergedState.gridRequestSiteOrigin ?? this.core.gridRequestSiteOrigin ?? null,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const sortedFrames = sortFramesByTargetProximity(frames, targetUnix);
|
|
194
|
+
const payload = {
|
|
195
|
+
runKey: satRunKey,
|
|
196
|
+
// Must match query params on each frame URL (same merge as buildSatelliteFetchParts) and mapsgl fetch headers.
|
|
197
|
+
apiKey: mergedState.apiKey || '',
|
|
198
|
+
userId: 'sdk-user',
|
|
199
|
+
// RN has no browser default Origin; native satellite fetch must mirror WeatherFrameProcessor / AguaceroCore grid headers.
|
|
200
|
+
gridRequestSiteOrigin:
|
|
201
|
+
typeof this.core.gridRequestSiteOrigin === 'string' ? this.core.gridRequestSiteOrigin : '',
|
|
202
|
+
bundleId: this.core.bundleId || '',
|
|
203
|
+
visible: stylePayload.visible,
|
|
204
|
+
opacity: stylePayload.opacity,
|
|
205
|
+
fillSmoothing: stylePayload.fillSmoothing,
|
|
206
|
+
frames: sortedFrames,
|
|
207
|
+
};
|
|
208
|
+
if (targetUnix != null && Number.isFinite(targetUnix)) {
|
|
209
|
+
payload.targetUnix = targetUnix;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const payloadJson = JSON.stringify(payload);
|
|
213
|
+
native.syncSatellite(payloadJson);
|
|
214
|
+
satBridgeWarn('SatelliteAndroidController.sync full payload sent', {
|
|
215
|
+
runKey: satRunKey,
|
|
216
|
+
timelineChanged,
|
|
217
|
+
frameCount: sortedFrames.length,
|
|
218
|
+
targetUnix: payload.targetUnix ?? null,
|
|
219
|
+
jsonChars: payloadJson.length,
|
|
220
|
+
});
|
|
221
|
+
this._cachedRunKey = satRunKey;
|
|
222
|
+
this._cachedTimelineSig = timelineSig;
|
|
223
|
+
this._lastTargetUnix = targetUnix != null && Number.isFinite(targetUnix) ? targetUnix : null;
|
|
224
|
+
this._lastStyleJson = styleJson;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Same timeline: avoid large JSON over the bridge — scrub + style deltas only.
|
|
229
|
+
const finiteTarget =
|
|
230
|
+
targetUnix != null && Number.isFinite(targetUnix) ? targetUnix : null;
|
|
231
|
+
if (finiteTarget !== this._lastTargetUnix) {
|
|
232
|
+
if (finiteTarget != null && native.activateSatelliteCachedUnix) {
|
|
233
|
+
native.activateSatelliteCachedUnix(finiteTarget);
|
|
234
|
+
satBridgeWarn('SatelliteAndroidController.activateSatelliteCachedUnix', { unix: finiteTarget });
|
|
235
|
+
}
|
|
236
|
+
this._lastTargetUnix = finiteTarget;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (styleJson !== this._lastStyleJson && native.updateSatelliteStyle) {
|
|
240
|
+
native.updateSatelliteStyle(styleJson);
|
|
241
|
+
satBridgeWarn('SatelliteAndroidController.updateSatelliteStyle', { jsonChars: styleJson.length });
|
|
242
|
+
this._lastStyleJson = styleJson;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|