@aguacerowx/react-native 0.0.52 → 0.0.54
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/LICENSE +21 -0
- package/android/src/main/cpp/satellite_ktx_jni.cpp +6 -1
- package/android/src/main/java/com/aguacerowx/reactnative/NexradRadarLayerView.java +14 -2
- package/android/src/main/java/com/aguacerowx/reactnative/SatelliteLayer.java +121 -1
- package/android/src/main/java/com/aguacerowx/reactnative/SatelliteLayerView.java +556 -392
- package/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +77 -0
- package/index.js +1 -1
- package/ios/SatelliteLayerView.swift +517 -517
- package/ios/WeatherFrameProcessorModule.m +19 -15
- package/ios/WeatherFrameProcessorModule.swift +65 -0
- package/lib/commonjs/AguaceroContext.js +0 -4
- package/lib/commonjs/AguaceroContext.js.map +1 -1
- package/lib/commonjs/GridRenderLayer.js +62 -76
- package/lib/commonjs/GridRenderLayer.js.map +1 -1
- package/lib/commonjs/MapManager.js +110 -224
- package/lib/commonjs/MapManager.js.map +1 -1
- package/lib/commonjs/MapRegistry.js +21 -33
- package/lib/commonjs/MapRegistry.js.map +1 -1
- package/lib/commonjs/NexradRadarLayer.android.js +28 -100
- package/lib/commonjs/NexradRadarLayer.android.js.map +1 -1
- package/lib/commonjs/NexradRadarLayer.ios.js +26 -97
- package/lib/commonjs/NexradRadarLayer.ios.js.map +1 -1
- package/lib/commonjs/NexradSitesMapLayer.js +41 -61
- package/lib/commonjs/NexradSitesMapLayer.js.map +1 -1
- package/lib/commonjs/SatelliteLayer.android.js +26 -38
- package/lib/commonjs/SatelliteLayer.android.js.map +1 -1
- package/lib/commonjs/SatelliteLayer.ios.js +30 -42
- package/lib/commonjs/SatelliteLayer.ios.js.map +1 -1
- package/lib/commonjs/StyleApplicator.js +129 -175
- package/lib/commonjs/StyleApplicator.js.map +1 -1
- package/lib/commonjs/WeatherLayerManager.js +996 -1627
- package/lib/commonjs/WeatherLayerManager.js.map +1 -1
- package/lib/commonjs/aguaceroCoreDebugHooks.js +58 -130
- package/lib/commonjs/aguaceroCoreDebugHooks.js.map +1 -1
- package/lib/commonjs/aguaceroRnDebug.js +147 -287
- package/lib/commonjs/aguaceroRnDebug.js.map +1 -1
- package/lib/commonjs/cdnAuthenticatedFetch.js +104 -0
- package/lib/commonjs/cdnAuthenticatedFetch.js.map +1 -0
- package/lib/commonjs/dispatchViewManagerCommandCompat.js +51 -88
- package/lib/commonjs/dispatchViewManagerCommandCompat.js.map +1 -1
- package/lib/commonjs/gridCdnAuth.js +41 -50
- package/lib/commonjs/gridCdnAuth.js.map +1 -1
- package/lib/commonjs/index.js +51 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/nexrad/nexradAndroidController.js +851 -863
- package/lib/commonjs/nexrad/nexradAndroidController.js.map +1 -1
- package/lib/commonjs/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js +62 -85
- package/lib/commonjs/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js.map +1 -1
- package/lib/commonjs/nexrad/nexradDiag.js +32 -148
- package/lib/commonjs/nexrad/nexradDiag.js.map +1 -1
- package/lib/commonjs/nexrad/nexradLevel2Keys.js +261 -0
- package/lib/commonjs/nexrad/nexradLevel2Keys.js.map +1 -0
- package/lib/commonjs/nexrad/nexradLutBuild.js +64 -111
- package/lib/commonjs/nexrad/nexradLutBuild.js.map +1 -1
- package/lib/commonjs/nexrad/nexradMapboxFrameOpts.bundled.js +136 -164
- package/lib/commonjs/nexrad/nexradMapboxFrameOpts.bundled.js.map +1 -1
- package/lib/commonjs/nexrad/nexradSdkImports.js +51 -0
- package/lib/commonjs/nexrad/nexradSdkImports.js.map +1 -0
- package/lib/commonjs/nexrad/radarArchiveCore.bundled.js +2848 -4455
- package/lib/commonjs/nexrad/radarArchiveCore.bundled.js.map +1 -1
- package/lib/commonjs/nexrad/radarDecode.worker.bundled.js +445 -648
- package/lib/commonjs/nexrad/radarDecode.worker.bundled.js.map +1 -1
- package/lib/commonjs/nexrad/radarFrameGpuMatch.bundled.js +52 -68
- package/lib/commonjs/nexrad/radarFrameGpuMatch.bundled.js.map +1 -1
- package/lib/commonjs/nexradNativeCommandIds.js +24 -0
- package/lib/commonjs/nexradNativeCommandIds.js.map +1 -0
- package/lib/commonjs/nws/NwsAlertsOverlay.android.js.map +1 -1
- package/lib/commonjs/nws/NwsAlertsOverlay.ios.js.map +1 -1
- package/lib/commonjs/nws/NwsAlertsOverlay.js +3 -3
- package/lib/commonjs/nws/NwsAlertsOverlay.js.map +1 -1
- package/lib/commonjs/nws/NwsAlertsOverlay.native.js +235 -361
- package/lib/commonjs/nws/NwsAlertsOverlay.native.js.map +1 -1
- package/lib/commonjs/nws/eventSourceRnPolyfill.js +92 -146
- package/lib/commonjs/nws/eventSourceRnPolyfill.js.map +1 -1
- package/lib/commonjs/nws/nwsAndroidConstants.js +2 -8
- package/lib/commonjs/nws/nwsAndroidConstants.js.map +1 -1
- package/lib/commonjs/satellite/satelliteAndroidController.js +122 -199
- package/lib/commonjs/satellite/satelliteAndroidController.js.map +1 -1
- package/lib/commonjs/satelliteBridgeDiag.js +3 -13
- package/lib/commonjs/satelliteBridgeDiag.js.map +1 -1
- package/lib/commonjs/satelliteRnDebug.js +160 -0
- package/lib/commonjs/satelliteRnDebug.js.map +1 -0
- package/lib/module/AguaceroContext.js +2 -7
- package/lib/module/AguaceroContext.js.map +1 -1
- package/lib/module/GridRenderLayer.js +66 -80
- package/lib/module/GridRenderLayer.js.map +1 -1
- package/lib/module/MapManager.js +125 -239
- package/lib/module/MapManager.js.map +1 -1
- package/lib/module/MapRegistry.js +21 -33
- package/lib/module/MapRegistry.js.map +1 -1
- package/lib/module/NexradRadarLayer.android.js +32 -104
- package/lib/module/NexradRadarLayer.android.js.map +1 -1
- package/lib/module/NexradRadarLayer.ios.js +30 -101
- package/lib/module/NexradRadarLayer.ios.js.map +1 -1
- package/lib/module/NexradSitesMapLayer.js +44 -63
- package/lib/module/NexradSitesMapLayer.js.map +1 -1
- package/lib/module/SatelliteLayer.android.js +32 -44
- package/lib/module/SatelliteLayer.android.js.map +1 -1
- package/lib/module/SatelliteLayer.ios.js +36 -48
- package/lib/module/SatelliteLayer.ios.js.map +1 -1
- package/lib/module/StyleApplicator.js +144 -191
- package/lib/module/StyleApplicator.js.map +1 -1
- package/lib/module/WeatherLayerManager.js +1024 -1655
- package/lib/module/WeatherLayerManager.js.map +1 -1
- package/lib/module/aguaceroCoreDebugHooks.js +59 -130
- package/lib/module/aguaceroCoreDebugHooks.js.map +1 -1
- package/lib/module/aguaceroRnDebug.js +151 -291
- package/lib/module/aguaceroRnDebug.js.map +1 -1
- package/lib/module/cdnAuthenticatedFetch.js +97 -0
- package/lib/module/cdnAuthenticatedFetch.js.map +1 -0
- package/lib/module/dispatchViewManagerCommandCompat.js +52 -90
- package/lib/module/dispatchViewManagerCommandCompat.js.map +1 -1
- package/lib/module/gridCdnAuth.js +38 -50
- package/lib/module/gridCdnAuth.js.map +1 -1
- package/lib/module/index.js +9 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/nexrad/nexradAndroidController.js +865 -876
- package/lib/module/nexrad/nexradAndroidController.js.map +1 -1
- package/lib/module/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js +62 -85
- package/lib/module/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js.map +1 -1
- package/lib/module/nexrad/nexradDiag.js +31 -145
- package/lib/module/nexrad/nexradDiag.js.map +1 -1
- package/lib/module/nexrad/nexradLevel2Keys.js +245 -0
- package/lib/module/nexrad/nexradLevel2Keys.js.map +1 -0
- package/lib/module/nexrad/nexradLutBuild.js +64 -110
- package/lib/module/nexrad/nexradLutBuild.js.map +1 -1
- package/lib/module/nexrad/nexradMapboxFrameOpts.bundled.js +136 -163
- package/lib/module/nexrad/nexradMapboxFrameOpts.bundled.js.map +1 -1
- package/lib/module/nexrad/nexradSdkImports.js +4 -0
- package/lib/module/nexrad/nexradSdkImports.js.map +1 -0
- package/lib/module/nexrad/radarArchiveCore.bundled.js +2839 -4448
- package/lib/module/nexrad/radarArchiveCore.bundled.js.map +1 -1
- package/lib/module/nexrad/radarDecode.worker.bundled.js +445 -648
- package/lib/module/nexrad/radarDecode.worker.bundled.js.map +1 -1
- package/lib/module/nexrad/radarFrameGpuMatch.bundled.js +50 -66
- package/lib/module/nexrad/radarFrameGpuMatch.bundled.js.map +1 -1
- package/lib/module/nexradNativeCommandIds.js +18 -0
- package/lib/module/nexradNativeCommandIds.js.map +1 -0
- package/lib/module/nws/NwsAlertsOverlay.android.js +1 -1
- package/lib/module/nws/NwsAlertsOverlay.android.js.map +1 -1
- package/lib/module/nws/NwsAlertsOverlay.ios.js +1 -1
- package/lib/module/nws/NwsAlertsOverlay.ios.js.map +1 -1
- package/lib/module/nws/NwsAlertsOverlay.js +5 -5
- package/lib/module/nws/NwsAlertsOverlay.js.map +1 -1
- package/lib/module/nws/NwsAlertsOverlay.native.js +248 -373
- package/lib/module/nws/NwsAlertsOverlay.native.js.map +1 -1
- package/lib/module/nws/eventSourceRnPolyfill.js +92 -146
- package/lib/module/nws/eventSourceRnPolyfill.js.map +1 -1
- package/lib/module/nws/nwsAndroidConstants.js +2 -8
- package/lib/module/nws/nwsAndroidConstants.js.map +1 -1
- package/lib/module/satellite/satelliteAndroidController.js +125 -202
- package/lib/module/satellite/satelliteAndroidController.js.map +1 -1
- package/lib/module/satelliteBridgeDiag.js +3 -13
- package/lib/module/satelliteBridgeDiag.js.map +1 -1
- package/lib/module/satelliteRnDebug.js +148 -0
- package/lib/module/satelliteRnDebug.js.map +1 -0
- package/lib/typescript/AguaceroContext.d.ts +0 -4
- package/lib/typescript/AguaceroContext.d.ts.map +1 -1
- package/lib/typescript/GridRenderLayer.d.ts.map +1 -1
- package/lib/typescript/MapManager.d.ts +0 -12
- package/lib/typescript/MapManager.d.ts.map +1 -1
- package/lib/typescript/MapRegistry.d.ts +10 -12
- package/lib/typescript/MapRegistry.d.ts.map +1 -1
- package/lib/typescript/NexradRadarLayer.android.d.ts.map +1 -1
- package/lib/typescript/NexradRadarLayer.ios.d.ts.map +1 -1
- package/lib/typescript/NexradSitesMapLayer.d.ts +4 -10
- package/lib/typescript/NexradSitesMapLayer.d.ts.map +1 -1
- package/lib/typescript/SatelliteLayer.android.d.ts.map +1 -1
- package/lib/typescript/SatelliteLayer.ios.d.ts.map +1 -1
- package/lib/typescript/StyleApplicator.d.ts +1 -1
- package/lib/typescript/StyleApplicator.d.ts.map +1 -1
- package/lib/typescript/WeatherLayerManager.d.ts.map +1 -1
- package/lib/typescript/aguaceroCoreDebugHooks.d.ts +2 -9
- package/lib/typescript/aguaceroCoreDebugHooks.d.ts.map +1 -1
- package/lib/typescript/aguaceroRnDebug.d.ts +47 -66
- package/lib/typescript/aguaceroRnDebug.d.ts.map +1 -1
- package/lib/typescript/cdnAuthenticatedFetch.d.ts +10 -0
- package/lib/typescript/cdnAuthenticatedFetch.d.ts.map +1 -0
- package/lib/typescript/dispatchViewManagerCommandCompat.d.ts +1 -17
- package/lib/typescript/dispatchViewManagerCommandCompat.d.ts.map +1 -1
- package/lib/typescript/gridCdnAuth.d.ts +16 -21
- package/lib/typescript/gridCdnAuth.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/nexrad/nexradAndroidController.d.ts +39 -89
- package/lib/typescript/nexrad/nexradAndroidController.d.ts.map +1 -1
- package/lib/typescript/nexrad/nexradCrossSectionSampleAtLatLon.bundled.d.ts +2 -1
- package/lib/typescript/nexrad/nexradCrossSectionSampleAtLatLon.bundled.d.ts.map +1 -1
- package/lib/typescript/nexrad/nexradDiag.d.ts +13 -101
- package/lib/typescript/nexrad/nexradDiag.d.ts.map +1 -1
- package/lib/typescript/nexrad/nexradLevel2Keys.d.ts +36 -0
- package/lib/typescript/nexrad/nexradLevel2Keys.d.ts.map +1 -0
- package/lib/typescript/nexrad/nexradLutBuild.d.ts +3 -10
- package/lib/typescript/nexrad/nexradLutBuild.d.ts.map +1 -1
- package/lib/typescript/nexrad/nexradMapboxFrameOpts.bundled.d.ts +4 -3
- package/lib/typescript/nexrad/nexradMapboxFrameOpts.bundled.d.ts.map +1 -1
- package/lib/typescript/nexrad/nexradSdkImports.d.ts +2 -0
- package/lib/typescript/nexrad/nexradSdkImports.d.ts.map +1 -0
- package/lib/typescript/nexrad/radarArchiveCore.bundled.d.ts +12 -7
- package/lib/typescript/nexrad/radarArchiveCore.bundled.d.ts.map +1 -1
- package/lib/typescript/nexrad/radarDecode.worker.bundled.d.ts +20 -20
- package/lib/typescript/nexrad/radarDecode.worker.bundled.d.ts.map +1 -1
- package/lib/typescript/nexrad/radarFrameGpuMatch.bundled.d.ts +4 -3
- package/lib/typescript/nexrad/radarFrameGpuMatch.bundled.d.ts.map +1 -1
- package/lib/typescript/nexradNativeCommandIds.d.ts +2 -0
- package/lib/typescript/nexradNativeCommandIds.d.ts.map +1 -0
- package/lib/typescript/nws/NwsAlertsOverlay.native.d.ts +6 -17
- package/lib/typescript/nws/NwsAlertsOverlay.native.d.ts.map +1 -1
- package/lib/typescript/nws/eventSourceRnPolyfill.d.ts +0 -3
- package/lib/typescript/nws/eventSourceRnPolyfill.d.ts.map +1 -1
- package/lib/typescript/nws/nwsAndroidConstants.d.ts +0 -5
- package/lib/typescript/nws/nwsAndroidConstants.d.ts.map +1 -1
- package/lib/typescript/satellite/satelliteAndroidController.d.ts +9 -47
- package/lib/typescript/satellite/satelliteAndroidController.d.ts.map +1 -1
- package/lib/typescript/satelliteBridgeDiag.d.ts +1 -5
- package/lib/typescript/satelliteBridgeDiag.d.ts.map +1 -1
- package/lib/typescript/satelliteRnDebug.d.ts +75 -0
- package/lib/typescript/satelliteRnDebug.d.ts.map +1 -0
- package/package.json +75 -74
- package/src/AguaceroContext.js +1 -7
- package/src/GridRenderLayer.js +1 -128
- package/src/MapManager.js +1 -277
- package/src/MapRegistry.js +1 -56
- package/src/NexradRadarLayer.android.js +1 -121
- package/src/NexradRadarLayer.ios.js +1 -115
- package/src/NexradSitesMapLayer.js +1 -75
- package/src/SatelliteLayer.android.js +1 -63
- package/src/SatelliteLayer.ios.js +1 -70
- package/src/StyleApplicator.js +1 -241
- package/src/WeatherLayerManager.js +1 -2025
- package/src/aguaceroCoreDebugHooks.js +1 -142
- package/src/aguaceroRnDebug.js +1 -335
- package/src/cdnAuthenticatedFetch.js +1 -0
- package/src/dispatchViewManagerCommandCompat.js +1 -100
- package/src/gridCdnAuth.js +1 -56
- package/src/index.js +1 -19
- package/src/nexrad/nexradAndroidController.js +1 -1078
- package/src/nexrad/nexradCrossSectionSampleAtLatLon.bundled.js +1 -91
- package/src/nexrad/nexradDiag.js +1 -150
- package/src/nexrad/nexradLevel2Keys.js +1 -0
- package/src/nexrad/nexradLutBuild.js +1 -126
- package/src/nexrad/nexradMapboxFrameOpts.bundled.js +1 -245
- package/src/nexrad/nexradSdkImports.js +1 -0
- package/src/nexrad/radarArchiveCore.bundled.js +1 -7085
- package/src/nexrad/radarDecode.worker.bundled.js +1 -813
- package/src/nexrad/radarFrameGpuMatch.bundled.js +1 -79
- package/src/nexradNativeCommandIds.js +1 -0
- package/src/nws/NwsAlertsOverlay.android.js +1 -1
- package/src/nws/NwsAlertsOverlay.ios.js +1 -1
- package/src/nws/NwsAlertsOverlay.js +1 -7
- package/src/nws/NwsAlertsOverlay.native.js +1 -463
- package/src/nws/eventSourceRnPolyfill.js +7 -193
- package/src/nws/nwsAndroidConstants.js +1 -8
- package/src/satellite/satelliteAndroidController.js +1 -245
- package/src/satelliteBridgeDiag.js +1 -15
- package/src/satelliteRnDebug.js +1 -0
- package/lib/commonjs/nexrad/nexradNativeCommandIds.js +0 -51
- package/lib/commonjs/nexrad/nexradNativeCommandIds.js.map +0 -1
- package/lib/module/nexrad/nexradNativeCommandIds.js +0 -44
- package/lib/module/nexrad/nexradNativeCommandIds.js.map +0 -1
- package/lib/typescript/nexrad/nexradNativeCommandIds.d.ts +0 -9
- package/lib/typescript/nexrad/nexradNativeCommandIds.d.ts.map +0 -1
- package/src/nexrad/nexradNativeCommandIds.js +0 -44
- /package/lib/commonjs/{nexrad/nexradSitesUs.json → nexradSitesUs.json} +0 -0
- /package/lib/module/{nexrad/nexradSitesUs.json → nexradSitesUs.json} +0 -0
- /package/src/{nexrad/nexradSitesUs.json → nexradSitesUs.json} +0 -0
|
@@ -1,1078 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AguaceroCore NEXRAD → Android native custom layer (parity with mapsgl {@link NexradWeatherController}).
|
|
3
|
-
*/
|
|
4
|
-
import { getUnitConversionFunction, getDefaultRadarTilt, nexradBinGroupIdForKey, variableToNexradGroup } from '@aguacerowx/javascript-sdk';
|
|
5
|
-
import { fromByteArray } from 'base64-js';
|
|
6
|
-
import {
|
|
7
|
-
fetchAndParseArchive,
|
|
8
|
-
objectKeyToUrl,
|
|
9
|
-
setNexradArchiveApiKey,
|
|
10
|
-
setNexradArchiveBundleId,
|
|
11
|
-
setNexradArchiveSiteOrigin,
|
|
12
|
-
setNexradSitesJsonUrl,
|
|
13
|
-
setNexradSitesFetchAuth,
|
|
14
|
-
} from './radarArchiveCore.bundled.js';
|
|
15
|
-
import { prepareRadarFrameForGpuReadout } from './radarFrameGpuMatch.bundled.js';
|
|
16
|
-
import { mapboxFrameUploadOptionsForNexradState } from './nexradMapboxFrameOpts.bundled.js';
|
|
17
|
-
import { sampleNexradFrameAtLatLon } from './nexradCrossSectionSampleAtLatLon.bundled.js';
|
|
18
|
-
import { buildNexradLutRgba } from './nexradLutBuild.js';
|
|
19
|
-
import { Platform } from 'react-native';
|
|
20
|
-
import { nexradDiagBootSnapshot, nexradDiagGateTextureSummary, nexradPerfSpan } from './nexradDiag.js';
|
|
21
|
-
import { aguaceroDebug, getAguaceroAuthDiagnosticSnapshot, isAguaceroRnDebugEnabled } from '../aguaceroRnDebug';
|
|
22
|
-
|
|
23
|
-
nexradDiagBootSnapshot({
|
|
24
|
-
archiveExports: {
|
|
25
|
-
setNexradArchiveApiKey: typeof setNexradArchiveApiKey,
|
|
26
|
-
setNexradArchiveBundleId: typeof setNexradArchiveBundleId,
|
|
27
|
-
objectKeyToUrl: typeof objectKeyToUrl,
|
|
28
|
-
fetchAndParseArchive: typeof fetchAndParseArchive,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
/** @returns {boolean} */
|
|
33
|
-
function ensureRadarArchiveBindings(label) {
|
|
34
|
-
const ok =
|
|
35
|
-
typeof setNexradArchiveApiKey === 'function' &&
|
|
36
|
-
typeof setNexradArchiveBundleId === 'function' &&
|
|
37
|
-
typeof objectKeyToUrl === 'function' &&
|
|
38
|
-
typeof fetchAndParseArchive === 'function';
|
|
39
|
-
return ok;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function pickNearestLevel3ObjectKey(unixTime, timeToKeyMap, maxDeltaSec = 600) {
|
|
43
|
-
const direct = timeToKeyMap[String(unixTime)];
|
|
44
|
-
if (direct) return direct;
|
|
45
|
-
const entries = Object.entries(timeToKeyMap || {});
|
|
46
|
-
if (entries.length === 0) return null;
|
|
47
|
-
let bestKey = null;
|
|
48
|
-
let bestDelta = Infinity;
|
|
49
|
-
for (const [tStr, key] of entries) {
|
|
50
|
-
const t = Number(tStr);
|
|
51
|
-
if (!Number.isFinite(t)) continue;
|
|
52
|
-
const d = Math.abs(t - unixTime);
|
|
53
|
-
if (d < bestDelta) {
|
|
54
|
-
bestDelta = d;
|
|
55
|
-
bestKey = key;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (bestKey == null) return null;
|
|
59
|
-
if (bestDelta > maxDeltaSec && Number.isFinite(maxDeltaSec)) return null;
|
|
60
|
-
return bestKey;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function isVelocityStyleRadarVar(radarVariable) {
|
|
64
|
-
return radarVariable === 'VEL' || radarVariable === 'SW' || radarVariable === 'N0G' || radarVariable === 'N0W';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function velocityRangeToMs(rangeMin, rangeMax, displayUnit) {
|
|
68
|
-
if (rangeMin == null || rangeMax == null || !displayUnit || displayUnit.toLowerCase() === 'm/s' || displayUnit.toLowerCase() === 'ms') {
|
|
69
|
-
return [rangeMin, rangeMax];
|
|
70
|
-
}
|
|
71
|
-
const toMs = getUnitConversionFunction(displayUnit, 'm/s', 'nexrad_vel');
|
|
72
|
-
if (!toMs) return [rangeMin, rangeMax];
|
|
73
|
-
return [toMs(rangeMin), toMs(rangeMax)];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function colormapToMs(colormap, radarVariable, displayUnit) {
|
|
77
|
-
if (!colormap || !Array.isArray(colormap) || colormap.length < 2) return colormap;
|
|
78
|
-
if (!isVelocityStyleRadarVar(radarVariable)) return colormap;
|
|
79
|
-
if (!displayUnit || displayUnit.toLowerCase() === 'm/s' || displayUnit.toLowerCase() === 'ms') return colormap;
|
|
80
|
-
const toMs = getUnitConversionFunction(
|
|
81
|
-
displayUnit,
|
|
82
|
-
'm/s',
|
|
83
|
-
radarVariable === 'SW' || radarVariable === 'N0W' ? 'nexrad_sw' : 'nexrad_vel',
|
|
84
|
-
);
|
|
85
|
-
if (!toMs) return colormap;
|
|
86
|
-
const out = [...colormap];
|
|
87
|
-
for (let i = 0; i < out.length; i += 2) {
|
|
88
|
-
const v = out[i];
|
|
89
|
-
if (typeof v === 'number' && Number.isFinite(v)) out[i] = toMs(v);
|
|
90
|
-
}
|
|
91
|
-
return out;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function radarShaderValueRange(rangeMin, rangeMax, radarVariable, units, nexradDataSource) {
|
|
95
|
-
const v = (radarVariable || '').toUpperCase();
|
|
96
|
-
if (isVelocityStyleRadarVar(v)) {
|
|
97
|
-
const [vmin, vmax] = velocityRangeToMs(rangeMin, rangeMax, units);
|
|
98
|
-
return [vmin ?? 0, vmax ?? 80];
|
|
99
|
-
}
|
|
100
|
-
if (nexradDataSource === 'level3' && (v === 'N0H' || v === 'HHC')) {
|
|
101
|
-
const lo = rangeMin ?? 0;
|
|
102
|
-
let hi = rangeMax ?? 11;
|
|
103
|
-
if (hi > 11) hi = 11;
|
|
104
|
-
if (hi < lo) hi = lo;
|
|
105
|
-
return [lo, hi];
|
|
106
|
-
}
|
|
107
|
-
if (v === 'KDP') {
|
|
108
|
-
const lo = rangeMin ?? -2;
|
|
109
|
-
let hi = rangeMax ?? 8;
|
|
110
|
-
if (hi < lo) hi = lo;
|
|
111
|
-
return [lo, hi];
|
|
112
|
-
}
|
|
113
|
-
return [rangeMin ?? 0, rangeMax ?? 80];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function nexradLevel3IsHydrometeorClassification(radarVariable) {
|
|
117
|
-
const v = (radarVariable || '').toUpperCase();
|
|
118
|
-
return v === 'N0H' || v === 'HHC';
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const NEXRAD_HYDROMETEOR_CLASS_LABELS = [
|
|
122
|
-
'Biological',
|
|
123
|
-
'Ground clutter',
|
|
124
|
-
'Ice crystals',
|
|
125
|
-
'Dry snow',
|
|
126
|
-
'Wet snow',
|
|
127
|
-
'Light rain',
|
|
128
|
-
'Heavy rain',
|
|
129
|
-
'Big drops',
|
|
130
|
-
'Graupel',
|
|
131
|
-
'Hail/rain',
|
|
132
|
-
'Large hail',
|
|
133
|
-
'Giant hail',
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
function nexradHydrometeorLabelForClassIndex(value) {
|
|
137
|
-
const idx = Math.round(Number(value));
|
|
138
|
-
if (idx >= 12) return null;
|
|
139
|
-
if (idx >= 0 && idx < NEXRAD_HYDROMETEOR_CLASS_LABELS.length) {
|
|
140
|
-
return NEXRAD_HYDROMETEOR_CLASS_LABELS[idx];
|
|
141
|
-
}
|
|
142
|
-
return `Class ${idx}`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function velocityMsToDisplay(valueMs, displayUnit) {
|
|
146
|
-
if (!displayUnit || displayUnit.toLowerCase() === 'm/s' || displayUnit.toLowerCase() === 'ms') {
|
|
147
|
-
return valueMs;
|
|
148
|
-
}
|
|
149
|
-
const fromMs = getUnitConversionFunction('m/s', displayUnit, 'nexrad_vel');
|
|
150
|
-
return fromMs ? fromMs(valueMs) : valueMs;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function beamHeightKm(slantRangeKm, elevDeg) {
|
|
154
|
-
const el = (elevDeg * Math.PI) / 180;
|
|
155
|
-
const ReKm = (6371000 * 4) / (3 * 1000);
|
|
156
|
-
const cosEl = Math.max(Math.cos(el), 0.05);
|
|
157
|
-
const s = slantRangeKm / cosEl;
|
|
158
|
-
return Math.sqrt(s * s + ReKm * ReKm + 2 * s * ReKm * Math.sin(el)) - ReKm;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function formatNexradInspectNumeric(raw, radarVariable) {
|
|
162
|
-
const vu = (radarVariable || '').toUpperCase();
|
|
163
|
-
const vl = radarVariable || '';
|
|
164
|
-
if (vu.includes('RATE') || vl.toLowerCase().includes('rate')) {
|
|
165
|
-
return Number(raw.toFixed(3).replace(/\.?0+$/, ''));
|
|
166
|
-
}
|
|
167
|
-
if (vu === 'RHO' || vu === 'ZDR' || vl.includes('RhoHV') || vl.includes('Zdr')) {
|
|
168
|
-
return Number(raw.toFixed(2));
|
|
169
|
-
}
|
|
170
|
-
if (Math.abs(raw) < 10) return Number(raw.toFixed(1));
|
|
171
|
-
return Math.round(raw);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Keep low so Level-2 decode stays off the critical path for touches/animations (was 4). */
|
|
175
|
-
/** iOS Metal uploads benefit from overlapping fetches; keep Android at 1 for steadier radio/CPU. */
|
|
176
|
-
const PRELOAD_CONCURRENCY = Platform.OS === 'ios' ? 2 : 1;
|
|
177
|
-
|
|
178
|
-
function yieldToJsEventLoop() {
|
|
179
|
-
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Match mapsgl {@link MapboxRadarLayer} geometry LRU — reuse serialized native payloads when scrubbing. */
|
|
183
|
-
const NATIVE_UPLOAD_JSON_LRU_MAX = 12;
|
|
184
|
-
|
|
185
|
-
function rleCompressGateData(gateData) {
|
|
186
|
-
const len = gateData.length;
|
|
187
|
-
const out = new Uint8Array(len * 2);
|
|
188
|
-
let outIdx = 0;
|
|
189
|
-
let i = 0;
|
|
190
|
-
while (i < len) {
|
|
191
|
-
let val0 = gateData[i];
|
|
192
|
-
let val1 = gateData[i + 1];
|
|
193
|
-
let count = 1;
|
|
194
|
-
let maxCount = (len - i) >> 1;
|
|
195
|
-
if (maxCount > 65535) maxCount = 65535;
|
|
196
|
-
|
|
197
|
-
let j = i + 2;
|
|
198
|
-
while (count < maxCount && gateData[j] === val0 && gateData[j + 1] === val1) {
|
|
199
|
-
count++;
|
|
200
|
-
j += 2;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
out[outIdx++] = count & 0xFF;
|
|
204
|
-
out[outIdx++] = (count >> 8) & 0xFF;
|
|
205
|
-
out[outIdx++] = val0;
|
|
206
|
-
out[outIdx++] = val1;
|
|
207
|
-
|
|
208
|
-
i = j;
|
|
209
|
-
}
|
|
210
|
-
return out.subarray(0, outIdx);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export class NexradAndroidController {
|
|
214
|
-
/**
|
|
215
|
-
* @param {*} core - AguaceroCore instance
|
|
216
|
-
* @param {React.MutableRefObject<{ uploadNexradFrame?: (s: string) => void; uploadNexradStyleOnly?: (s: string) => void; clearNexrad?: () => void; activateNexradCachedFrame?: (k: string) => void } | null>} layerRef
|
|
217
|
-
* @param {object} [options]
|
|
218
|
-
*/
|
|
219
|
-
constructor(core, layerRef, options = {}) {
|
|
220
|
-
this.core = core;
|
|
221
|
-
this._layerRef = layerRef;
|
|
222
|
-
this._lastSyncKey = null;
|
|
223
|
-
this._abort = null;
|
|
224
|
-
this._interpolateColormap = options.interpolateNexradColormap !== false;
|
|
225
|
-
this._gateSmoothing = options.nexradGateSmoothing === true;
|
|
226
|
-
this._frameCache = new Map();
|
|
227
|
-
/** WeakMap: decoded frame → Map(uploadOptsKey → prepared frame). Avoids O(n²) canonical re-bin every map move. */
|
|
228
|
-
this._gpuReadoutPrep = new WeakMap();
|
|
229
|
-
this._preloadAbort = null;
|
|
230
|
-
this._preloadTimelineSig = null;
|
|
231
|
-
this._nativeFrameUploaded = false;
|
|
232
|
-
/** @type {{ valueScale: number; valueOffset: number } | null} */
|
|
233
|
-
this._lastUploadMeta = null;
|
|
234
|
-
/** @type {Map<string, { json: string; valueScale: number; valueOffset: number }>} */
|
|
235
|
-
this._nativeUploadJsonLru = new Map();
|
|
236
|
-
/** @type {Set<string>} Sync keys with gate texture registered in Android GL cache (AguaceroNexradGpuFrameReady). */
|
|
237
|
-
this._nativeGpuReadyKeys = new Set();
|
|
238
|
-
/** @type {Map<string, { valueScale: number; valueOffset: number }>} */
|
|
239
|
-
this._uploadMetaBySyncKey = new Map();
|
|
240
|
-
/** @type {{ remove: () => void } | null} */
|
|
241
|
-
this._gpuReadyEventSub = null;
|
|
242
|
-
try {
|
|
243
|
-
const { DeviceEventEmitter } = require('react-native');
|
|
244
|
-
if (DeviceEventEmitter && typeof DeviceEventEmitter.addListener === 'function') {
|
|
245
|
-
this._gpuReadyEventSub = DeviceEventEmitter.addListener('AguaceroNexradGpuFrameReady', (e) => {
|
|
246
|
-
const k = e && e.nativeGpuCacheKey;
|
|
247
|
-
if (typeof k === 'string' && k.length > 0) {
|
|
248
|
-
this._nativeGpuReadyKeys.add(k);
|
|
249
|
-
this._trimNativeGpuReadyKeys(96);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
} catch (err) {
|
|
254
|
-
/* ignore */
|
|
255
|
-
}
|
|
256
|
-
const base = typeof core?.baseGridUrl === 'string' ? core.baseGridUrl.replace(/\/$/, '') : '';
|
|
257
|
-
if (base && typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
258
|
-
setNexradSitesJsonUrl(`${base}/data/nexrad.json`);
|
|
259
|
-
setNexradSitesFetchAuth(core.apiKey || '', core.bundleId || '');
|
|
260
|
-
}
|
|
261
|
-
setNexradArchiveSiteOrigin(core.gridRequestSiteOrigin || 'https://localhost');
|
|
262
|
-
if (isAguaceroRnDebugEnabled()) {
|
|
263
|
-
aguaceroDebug('nexrad.authConfigured', getAguaceroAuthDiagnosticSnapshot(core, { phase: 'NexradAndroidController.constructor' }));
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
_trimNativeGpuReadyKeys(max) {
|
|
268
|
-
while (this._nativeGpuReadyKeys.size > max) {
|
|
269
|
-
const first = this._nativeGpuReadyKeys.values().next().value;
|
|
270
|
-
if (first === undefined) break;
|
|
271
|
-
this._nativeGpuReadyKeys.delete(first);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
_rememberUploadMetaForSyncKey(syncKey, valueScale, valueOffset) {
|
|
276
|
-
this._uploadMetaBySyncKey.set(syncKey, { valueScale, valueOffset });
|
|
277
|
-
while (this._uploadMetaBySyncKey.size > 40) {
|
|
278
|
-
const k = this._uploadMetaBySyncKey.keys().next().value;
|
|
279
|
-
if (k === undefined) break;
|
|
280
|
-
this._uploadMetaBySyncKey.delete(k);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
updateStyleOptions(options) {
|
|
285
|
-
if (options.interpolateNexradColormap != null) {
|
|
286
|
-
this._interpolateColormap = options.interpolateNexradColormap !== false;
|
|
287
|
-
}
|
|
288
|
-
if (options.nexradGateSmoothing != null) {
|
|
289
|
-
this._gateSmoothing = options.nexradGateSmoothing === true;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
destroy() {
|
|
294
|
-
try {
|
|
295
|
-
this._gpuReadyEventSub?.remove();
|
|
296
|
-
} catch (_) {
|
|
297
|
-
/* ignore */
|
|
298
|
-
}
|
|
299
|
-
this._gpuReadyEventSub = null;
|
|
300
|
-
this._nativeGpuReadyKeys.clear();
|
|
301
|
-
this._uploadMetaBySyncKey.clear();
|
|
302
|
-
this._abort?.abort();
|
|
303
|
-
this._abort = null;
|
|
304
|
-
this._preloadAbort?.abort();
|
|
305
|
-
this._preloadAbort = null;
|
|
306
|
-
this._frameCache.clear();
|
|
307
|
-
this._gpuReadoutPrep = new WeakMap();
|
|
308
|
-
this._preloadTimelineSig = null;
|
|
309
|
-
this._lastSyncKey = null;
|
|
310
|
-
this._nativeFrameUploaded = false;
|
|
311
|
-
this._lastUploadMeta = null;
|
|
312
|
-
this._nativeUploadJsonLru.clear();
|
|
313
|
-
this._layerRef?.current?.clearNexrad?.();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
_getPreparedReadoutFrame(frame, uploadOpts) {
|
|
317
|
-
let byOpts = this._gpuReadoutPrep.get(frame);
|
|
318
|
-
if (!byOpts) {
|
|
319
|
-
byOpts = new Map();
|
|
320
|
-
this._gpuReadoutPrep.set(frame, byOpts);
|
|
321
|
-
}
|
|
322
|
-
const optKey =
|
|
323
|
-
uploadOpts && typeof uploadOpts === 'object'
|
|
324
|
-
? JSON.stringify(uploadOpts)
|
|
325
|
-
: String(uploadOpts);
|
|
326
|
-
let prepared = byOpts.get(optKey);
|
|
327
|
-
if (!prepared) {
|
|
328
|
-
prepared = prepareRadarFrameForGpuReadout(frame, uploadOpts);
|
|
329
|
-
byOpts.set(optKey, prepared);
|
|
330
|
-
}
|
|
331
|
-
return prepared;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
_native() {
|
|
335
|
-
return this._layerRef?.current;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
_lutPack(state) {
|
|
339
|
-
const radarVar = (state.nexradProduct || 'REF').toUpperCase();
|
|
340
|
-
const colormapFlat = state.colormap;
|
|
341
|
-
const colormapForShader = colormapToMs(colormapFlat, radarVar, state.units);
|
|
342
|
-
const [shaderMin, shaderMax] = radarShaderValueRange(
|
|
343
|
-
colormapFlat?.[0],
|
|
344
|
-
colormapFlat?.[colormapFlat.length - 2],
|
|
345
|
-
radarVar,
|
|
346
|
-
state.units,
|
|
347
|
-
state.nexradDataSource,
|
|
348
|
-
);
|
|
349
|
-
const { bytes, discreteIntegerLut } = buildNexradLutRgba(
|
|
350
|
-
colormapForShader,
|
|
351
|
-
shaderMin,
|
|
352
|
-
shaderMax,
|
|
353
|
-
this._interpolateColormap,
|
|
354
|
-
);
|
|
355
|
-
return {
|
|
356
|
-
lutB64: fromByteArray(bytes),
|
|
357
|
-
lutMin: shaderMin,
|
|
358
|
-
lutMax: shaderMax,
|
|
359
|
-
discreteIntegerLut,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* LRU for full native JSON payloads (parity with mapsgl gate-texture reuse — avoids base64 + stringify on repeat scrubs).
|
|
365
|
-
* @param {string} syncKey
|
|
366
|
-
* @returns {{ json: string; valueScale: number; valueOffset: number } | null}
|
|
367
|
-
*/
|
|
368
|
-
_nativeUploadJsonLruTouch(syncKey) {
|
|
369
|
-
const ent = this._nativeUploadJsonLru.get(syncKey);
|
|
370
|
-
if (!ent) return null;
|
|
371
|
-
this._nativeUploadJsonLru.delete(syncKey);
|
|
372
|
-
this._nativeUploadJsonLru.set(syncKey, ent);
|
|
373
|
-
return ent;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/** @param {string} syncKey @param {{ json: string; valueScale: number; valueOffset: number }} entry */
|
|
377
|
-
_nativeUploadJsonLruPut(syncKey, entry) {
|
|
378
|
-
if (this._nativeUploadJsonLru.has(syncKey)) this._nativeUploadJsonLru.delete(syncKey);
|
|
379
|
-
this._nativeUploadJsonLru.set(syncKey, entry);
|
|
380
|
-
while (this._nativeUploadJsonLru.size > NATIVE_UPLOAD_JSON_LRU_MAX) {
|
|
381
|
-
const oldest = this._nativeUploadJsonLru.keys().next().value;
|
|
382
|
-
if (oldest === undefined) break;
|
|
383
|
-
this._nativeUploadJsonLru.delete(oldest);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Full-frame JSON includes LUT + opacity + shader toggles; keep LRU key aligned so scrubbing
|
|
389
|
-
* cannot resurrect an outdated style after the user changes only presentation options.
|
|
390
|
-
*/
|
|
391
|
-
_nativeUploadLruKey(state, syncKey) {
|
|
392
|
-
return `${syncKey}|o:${state.opacity ?? 1}|g:${this._gateSmoothing ? 1 : 0}|i:${this._interpolateColormap ? 1 : 0}`;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* After JS decoded a frame into `_frameCache`, optionally push it through the native bridge.
|
|
397
|
-
* During timeline preload we **only** upload the **currently displayed** unix to Metal; other
|
|
398
|
-
* volumes stay in the JS cache until {@link sync} needs them (avoids 40+ full decodes on load).
|
|
399
|
-
*/
|
|
400
|
-
_primeNativeGpuUploadIfNeeded(snapshot, unix, p, frame, abortSignal) {
|
|
401
|
-
if (!frame || !p || (abortSignal && abortSignal.aborted)) return;
|
|
402
|
-
try {
|
|
403
|
-
const displayUx =
|
|
404
|
-
snapshot.nexradTimestamp != null && Number.isFinite(Number(snapshot.nexradTimestamp))
|
|
405
|
-
? Number(snapshot.nexradTimestamp)
|
|
406
|
-
: NaN;
|
|
407
|
-
if (Number.isFinite(displayUx) && Number(unix) !== displayUx) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const syncKey = this._syncIdentity(snapshot, p, unix);
|
|
411
|
-
if (this._nativeGpuReadyKeys.has(syncKey)) {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
if (!this._native()?.uploadNexradFrame) {
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
this._uploadFrameToNative(snapshot, frame, p, unix);
|
|
418
|
-
} catch (err) {
|
|
419
|
-
/* ignore */
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* @param {*} state
|
|
425
|
-
* @param {*} frame - decoded archive frame
|
|
426
|
-
* @param {{ fetchKey: string }} p - from {@link _buildFetchParamsForUnix}
|
|
427
|
-
* @param {number} unix
|
|
428
|
-
*/
|
|
429
|
-
_uploadFrameToNative(state, frame, p, unix) {
|
|
430
|
-
const uploadTotal = nexradPerfSpan(
|
|
431
|
-
`uploadFrame.total site=${state.nexradSite} unix=${unix} var=${p?.radarVar ?? state.nexradProduct}`,
|
|
432
|
-
);
|
|
433
|
-
const n = this._native();
|
|
434
|
-
if (!n?.uploadNexradFrame) {
|
|
435
|
-
uploadTotal.end({ outcome: 'skippedNoNative' });
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const syncKey = this._syncIdentity(state, p, unix);
|
|
440
|
-
const lruKey = this._nativeUploadLruKey(state, syncKey);
|
|
441
|
-
const lruHit = this._nativeUploadJsonLruTouch(lruKey);
|
|
442
|
-
if (lruHit) {
|
|
443
|
-
this._lastUploadMeta = { valueScale: lruHit.valueScale, valueOffset: lruHit.valueOffset };
|
|
444
|
-
this._rememberUploadMetaForSyncKey(syncKey, lruHit.valueScale, lruHit.valueOffset);
|
|
445
|
-
if (n.activateNexradCachedFrame && this._nativeGpuReadyKeys.has(syncKey)) {
|
|
446
|
-
const tAct = nexradPerfSpan('uploadFrame.nativeGpuActivateOnly');
|
|
447
|
-
n.activateNexradCachedFrame(syncKey);
|
|
448
|
-
tAct.end({ keyLen: syncKey.length });
|
|
449
|
-
const tStyle = nexradPerfSpan('uploadFrame.styleAfterGpuActivate');
|
|
450
|
-
this.applyStyleFromState(state);
|
|
451
|
-
tStyle.end({});
|
|
452
|
-
this._nativeFrameUploaded = true;
|
|
453
|
-
uploadTotal.end({ outcome: 'gpuActivateOnly', jsonCharsSkipped: lruHit.json.length });
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
const bridge = nexradPerfSpan('uploadFrame.nativeJsonLru.bridgeDispatch');
|
|
457
|
-
n.uploadNexradFrame(lruHit.json);
|
|
458
|
-
bridge.end({ jsonChars: lruHit.json.length, path: 'jsonLruHit' });
|
|
459
|
-
this._nativeFrameUploaded = true;
|
|
460
|
-
uploadTotal.end({ outcome: 'jsonLruHit', jsonChars: lruHit.json.length });
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const tOpts = nexradPerfSpan('uploadFrame.mapboxFrameUploadOptions');
|
|
465
|
-
const uploadOpts = mapboxFrameUploadOptionsForNexradState(state);
|
|
466
|
-
tOpts.end({});
|
|
467
|
-
|
|
468
|
-
// Skip JS sorting for Android native upload
|
|
469
|
-
// We pass the raw frame and let Android do the sorting/canonicalization
|
|
470
|
-
const rawFrame = frame;
|
|
471
|
-
|
|
472
|
-
this._lastUploadMeta = {
|
|
473
|
-
valueScale: rawFrame.valueScale,
|
|
474
|
-
valueOffset: rawFrame.valueOffset,
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
const tLut = nexradPerfSpan('uploadFrame.lutPack_buildNexradLutRgba');
|
|
478
|
-
const style = this._lutPack(state);
|
|
479
|
-
tLut.end({
|
|
480
|
-
lutB64Chars: style.lutB64?.length ?? 0,
|
|
481
|
-
discreteIntegerLut: style.discreteIntegerLut,
|
|
482
|
-
lutMin: style.lutMin,
|
|
483
|
-
lutMax: style.lutMax,
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
const tRay = nexradPerfSpan('uploadFrame.rayBoundariesToJsArray');
|
|
487
|
-
const rayBoundaries = rawFrame.rayBoundariesDeg?.slice ? Array.from(rawFrame.rayBoundariesDeg) : [...(rawFrame.rayBoundariesDeg || [])];
|
|
488
|
-
tRay.end({ len: rayBoundaries.length });
|
|
489
|
-
|
|
490
|
-
const tB64 = nexradPerfSpan('uploadFrame.gateData_toBase64');
|
|
491
|
-
const rleGateData = rleCompressGateData(rawFrame.gateData);
|
|
492
|
-
const gateB64 = fromByteArray(rleGateData);
|
|
493
|
-
tB64.end({ gateB64Chars: gateB64.length, rleBytes: rleGateData.length, rawBytes: rawFrame.gateData.length });
|
|
494
|
-
|
|
495
|
-
const payload = {
|
|
496
|
-
gateB64,
|
|
497
|
-
gateIsRle: true,
|
|
498
|
-
nGates: rawFrame.nGates,
|
|
499
|
-
nRays: rawFrame.nRays,
|
|
500
|
-
stationLat: rawFrame.stationLat,
|
|
501
|
-
stationLon: rawFrame.stationLon,
|
|
502
|
-
firstGateKm: rawFrame.firstGateKm,
|
|
503
|
-
gateWidthKm: rawFrame.gateWidthKm,
|
|
504
|
-
rayBoundaries,
|
|
505
|
-
lutB64: style.lutB64,
|
|
506
|
-
valueScale: rawFrame.valueScale,
|
|
507
|
-
valueOffset: rawFrame.valueOffset,
|
|
508
|
-
lutMin: style.lutMin,
|
|
509
|
-
lutMax: style.lutMax,
|
|
510
|
-
discreteIntegerLut: style.discreteIntegerLut,
|
|
511
|
-
opacity: state.opacity ?? 1,
|
|
512
|
-
gateSmoothPolar: this._gateSmoothing ? 1 : 0,
|
|
513
|
-
interpolateLut: this._interpolateColormap ? 1 : 0,
|
|
514
|
-
nativeGpuCacheKey: syncKey,
|
|
515
|
-
uploadOpts: uploadOpts,
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
const tJson = nexradPerfSpan('uploadFrame.JSON_stringify_payload');
|
|
519
|
-
const json = JSON.stringify(payload);
|
|
520
|
-
tJson.end({
|
|
521
|
-
jsonChars: json.length,
|
|
522
|
-
rayBoundaryLen: rayBoundaries.length,
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
const tLruPut = nexradPerfSpan('uploadFrame.nativeUploadJsonLruPut');
|
|
526
|
-
this._nativeUploadJsonLruPut(lruKey, {
|
|
527
|
-
json,
|
|
528
|
-
valueScale: rawFrame.valueScale,
|
|
529
|
-
valueOffset: rawFrame.valueOffset,
|
|
530
|
-
});
|
|
531
|
-
tLruPut.end({ lruKeyTail: lruKey.slice(-96), lruSize: this._nativeUploadJsonLru.size });
|
|
532
|
-
|
|
533
|
-
const gateSummary = nexradDiagGateTextureSummary(
|
|
534
|
-
rawFrame.gateData,
|
|
535
|
-
rawFrame.nGates,
|
|
536
|
-
rawFrame.nRays,
|
|
537
|
-
rawFrame.valueScale,
|
|
538
|
-
rawFrame.valueOffset,
|
|
539
|
-
style.lutMin,
|
|
540
|
-
style.lutMax,
|
|
541
|
-
);
|
|
542
|
-
|
|
543
|
-
const tBridge = nexradPerfSpan('uploadFrame.nativeBridge_uploadNexradFrame');
|
|
544
|
-
n.uploadNexradFrame(json);
|
|
545
|
-
tBridge.end({ jsonChars: json.length, path: 'fullRebuild' });
|
|
546
|
-
|
|
547
|
-
this._rememberUploadMetaForSyncKey(syncKey, rawFrame.valueScale, rawFrame.valueOffset);
|
|
548
|
-
this._nativeFrameUploaded = true;
|
|
549
|
-
uploadTotal.end({
|
|
550
|
-
outcome: 'fullPipeline',
|
|
551
|
-
jsonChars: json.length,
|
|
552
|
-
nGates: rawFrame.nGates,
|
|
553
|
-
nRays: rawFrame.nRays,
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
applyStyleFromState(state) {
|
|
558
|
-
const perf = nexradPerfSpan(
|
|
559
|
-
`applyStyleFromState site=${state.nexradSite} product=${state.nexradProduct} tilt=${state.nexradTilt}`,
|
|
560
|
-
);
|
|
561
|
-
const n = this._native();
|
|
562
|
-
if (!this._nativeFrameUploaded || !this._lastUploadMeta || !n?.uploadNexradStyleOnly) {
|
|
563
|
-
perf.end({
|
|
564
|
-
outcome: 'skipped',
|
|
565
|
-
nativeFrameUploaded: this._nativeFrameUploaded,
|
|
566
|
-
hasUploadMeta: !!this._lastUploadMeta,
|
|
567
|
-
hasStyleMethod: !!n?.uploadNexradStyleOnly,
|
|
568
|
-
});
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const tLut = nexradPerfSpan('applyStyleFromState.lutPack');
|
|
573
|
-
const style = this._lutPack(state);
|
|
574
|
-
tLut.end({ lutB64Chars: style.lutB64?.length ?? 0 });
|
|
575
|
-
|
|
576
|
-
const payload = {
|
|
577
|
-
lutB64: style.lutB64,
|
|
578
|
-
valueScale: this._lastUploadMeta.valueScale,
|
|
579
|
-
valueOffset: this._lastUploadMeta.valueOffset,
|
|
580
|
-
lutMin: style.lutMin,
|
|
581
|
-
lutMax: style.lutMax,
|
|
582
|
-
discreteIntegerLut: style.discreteIntegerLut,
|
|
583
|
-
opacity: state.opacity ?? 1,
|
|
584
|
-
gateSmoothPolar: this._gateSmoothing ? 1 : 0,
|
|
585
|
-
interpolateLut: this._interpolateColormap ? 1 : 0,
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const tJson = nexradPerfSpan('applyStyleFromState.JSON_stringify');
|
|
589
|
-
const json = JSON.stringify(payload);
|
|
590
|
-
tJson.end({ jsonChars: json.length });
|
|
591
|
-
|
|
592
|
-
const tBridge = nexradPerfSpan('applyStyleFromState.nativeBridge');
|
|
593
|
-
n.uploadNexradStyleOnly(json);
|
|
594
|
-
tBridge.end({ jsonChars: json.length });
|
|
595
|
-
|
|
596
|
-
perf.end({ outcome: 'dispatched' });
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
_resolveListingContext(state) {
|
|
600
|
-
const nk = this.core._nexradTimesCacheKey?.();
|
|
601
|
-
const ent = nk ? this.core.nexradTimesByStation?.[nk] : null;
|
|
602
|
-
// Merge so we still resolve object keys if the last state event omitted maps but the core cache is populated.
|
|
603
|
-
const timeToKeyMap = { ...(ent?.timeToKeyMap || {}), ...(state.nexradTimeToKeyMap || {}) };
|
|
604
|
-
const motionMap = {
|
|
605
|
-
...(ent?.level3MotionTimeToKeyMap || {}),
|
|
606
|
-
...(state.nexradLevel3MotionTimeToKeyMap || {}),
|
|
607
|
-
};
|
|
608
|
-
return { nk, ent, timeToKeyMap, motionMap };
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
_buildFetchParamsForUnix(state, unix) {
|
|
612
|
-
const radarVar = (state.nexradProduct || 'REF').toUpperCase();
|
|
613
|
-
const radarSource = state.nexradDataSource === 'level3' ? 'level3' : 'level2';
|
|
614
|
-
const groupId = nexradBinGroupIdForKey(variableToNexradGroup(radarVar));
|
|
615
|
-
const { timeToKeyMap, motionMap } = this._resolveListingContext(state);
|
|
616
|
-
const objectKey = timeToKeyMap[String(unix)];
|
|
617
|
-
if (!objectKey) {
|
|
618
|
-
return null;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
let motionObjectKey = null;
|
|
622
|
-
if (radarVar === 'VEL' && state.nexradStormRelative) {
|
|
623
|
-
motionObjectKey = pickNearestLevel3ObjectKey(unix, motionMap);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
const url = objectKeyToUrl(objectKey, radarSource);
|
|
627
|
-
const fetchKey = `${url}|${radarVar}|${radarSource}|${motionObjectKey || ''}`;
|
|
628
|
-
return { objectKey, url, fetchKey, motionObjectKey, radarVar, radarSource, groupId };
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
async _fetchFrame(state, unix, { signal, priority }) {
|
|
632
|
-
const fetchPerf = nexradPerfSpan(
|
|
633
|
-
`fetchAndParseArchive priority=${priority} site=${state.nexradSite} unix=${unix} product=${state.nexradProduct}`,
|
|
634
|
-
);
|
|
635
|
-
const p = this._buildFetchParamsForUnix(state, unix);
|
|
636
|
-
if (!p) {
|
|
637
|
-
fetchPerf.end({ ok: false, reason: 'noFetchParams' });
|
|
638
|
-
return null;
|
|
639
|
-
}
|
|
640
|
-
try {
|
|
641
|
-
const frame = await fetchAndParseArchive(p.url, p.objectKey, p.radarVar, p.groupId, p.radarSource, {
|
|
642
|
-
signal,
|
|
643
|
-
priority,
|
|
644
|
-
level3MotionObjectKey: p.motionObjectKey,
|
|
645
|
-
});
|
|
646
|
-
fetchPerf.end({
|
|
647
|
-
ok: !!frame,
|
|
648
|
-
radarVar: p.radarVar,
|
|
649
|
-
radarSource: p.radarSource,
|
|
650
|
-
objectKeyTail: (p.objectKey || '').slice(-48),
|
|
651
|
-
motionKeyTail: (p.motionObjectKey || '').slice(-48),
|
|
652
|
-
});
|
|
653
|
-
return frame;
|
|
654
|
-
} catch (err) {
|
|
655
|
-
fetchPerf.end({
|
|
656
|
-
ok: false,
|
|
657
|
-
error: err?.message || String(err),
|
|
658
|
-
radarVar: p.radarVar,
|
|
659
|
-
radarSource: p.radarSource,
|
|
660
|
-
});
|
|
661
|
-
throw err;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
_preloadTimelineSignature(state) {
|
|
666
|
-
const nk = this.core._nexradTimesCacheKey?.() || '';
|
|
667
|
-
const ts = [...(state.availableNexradTimestamps || [])]
|
|
668
|
-
.map(Number)
|
|
669
|
-
.filter((t) => Number.isFinite(t))
|
|
670
|
-
.sort((a, b) => a - b)
|
|
671
|
-
.join(',');
|
|
672
|
-
const tilt =
|
|
673
|
-
state.nexradTilt != null && Number.isFinite(Number(state.nexradTilt))
|
|
674
|
-
? Number(state.nexradTilt).toFixed(3)
|
|
675
|
-
: '';
|
|
676
|
-
return `${nk}|${ts}|sr:${state.nexradStormRelative ? 1 : 0}|tilt:${tilt}`;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
_syncIdentity(state, p, unix) {
|
|
680
|
-
const tilt =
|
|
681
|
-
state.nexradTilt != null && Number.isFinite(Number(state.nexradTilt))
|
|
682
|
-
? Number(state.nexradTilt).toFixed(3)
|
|
683
|
-
: '';
|
|
684
|
-
return `${p.fetchKey}|tilt:${tilt}|u:${unix}`;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
preloadAllAvailable(state) {
|
|
688
|
-
if (!state.isNexrad || !state.nexradSite) return;
|
|
689
|
-
|
|
690
|
-
if (!ensureRadarArchiveBindings('preloadAllAvailable')) return;
|
|
691
|
-
|
|
692
|
-
const times = [...(state.availableNexradTimestamps || [])]
|
|
693
|
-
.map(Number)
|
|
694
|
-
.filter((t) => Number.isFinite(t));
|
|
695
|
-
if (!times.length) {
|
|
696
|
-
// Important: do not touch `_preloadTimelineSig` or the frame cache here — callers
|
|
697
|
-
// sometimes pass `core.state` before `availableNexradTimestamps` is committed; an
|
|
698
|
-
// empty list must not poison the signature or wipe the LRU (see WeatherLayerManager
|
|
699
|
-
// preload kick merging subscription snapshot).
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const sig = this._preloadTimelineSignature(state);
|
|
704
|
-
if (sig === this._preloadTimelineSig) {
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
this._preloadTimelineSig = sig;
|
|
709
|
-
this._preloadAbort?.abort();
|
|
710
|
-
this._preloadAbort = new AbortController();
|
|
711
|
-
const signal = this._preloadAbort.signal;
|
|
712
|
-
|
|
713
|
-
// Keep the volume currently on screen in the JS cache so getInspectPayload (mapsgl parity readouts)
|
|
714
|
-
// still works while we clear and rebuild the prefetch LRU — otherwise readouts stay null until
|
|
715
|
-
// this unix is fetched again (often last in the timeline).
|
|
716
|
-
const curUnix =
|
|
717
|
-
state.nexradTimestamp != null && Number.isFinite(Number(state.nexradTimestamp))
|
|
718
|
-
? Number(state.nexradTimestamp)
|
|
719
|
-
: NaN;
|
|
720
|
-
|
|
721
|
-
// Keep frames that are still in the new timeline (or the currently displayed one)
|
|
722
|
-
// to avoid re-fetching them when expanding the duration window.
|
|
723
|
-
const validFetchKeys = new Set();
|
|
724
|
-
for (const unix of times) {
|
|
725
|
-
const p = this._buildFetchParamsForUnix(state, unix);
|
|
726
|
-
if (p) validFetchKeys.add(p.fetchKey);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const curP = Number.isFinite(curUnix) ? this._buildFetchParamsForUnix(state, curUnix) : null;
|
|
730
|
-
if (curP) validFetchKeys.add(curP.fetchKey);
|
|
731
|
-
|
|
732
|
-
for (const key of this._frameCache.keys()) {
|
|
733
|
-
if (!validFetchKeys.has(key)) {
|
|
734
|
-
this._frameCache.delete(key);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
// Do NOT clear `_nativeGpuReadyKeys` / JSON LRU / upload meta here: that forced a full
|
|
738
|
-
// ~1MB JSON parse + native decode on every timeline scrub after prefetch. GPU cache keys
|
|
739
|
-
// stay valid while Metal still holds the slot; `sync` falls back to full upload if needed.
|
|
740
|
-
// (Controller `destroy()` still clears everything on site/mode teardown.)
|
|
741
|
-
|
|
742
|
-
setNexradArchiveApiKey(this.core.apiKey || '');
|
|
743
|
-
setNexradArchiveBundleId(this.core.bundleId || '');
|
|
744
|
-
setNexradArchiveSiteOrigin(this.core.gridRequestSiteOrigin || 'https://localhost');
|
|
745
|
-
setNexradSitesFetchAuth(this.core.apiKey || '', this.core.bundleId || '');
|
|
746
|
-
if (isAguaceroRnDebugEnabled()) {
|
|
747
|
-
aguaceroDebug('nexrad.authConfigured', getAguaceroAuthDiagnosticSnapshot(this.core, {
|
|
748
|
-
phase: 'NexradAndroidController.preload',
|
|
749
|
-
site: state.nexradSite,
|
|
750
|
-
product: state.nexradProduct,
|
|
751
|
-
}));
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
const snapshot = { ...state };
|
|
755
|
-
const preloadBatch = nexradPerfSpan(`preload.batch site=${state.nexradSite} times=${times.length}`);
|
|
756
|
-
let preloadCompleted = 0;
|
|
757
|
-
let preloadFailed = 0;
|
|
758
|
-
let preloadSkippedCached = 0;
|
|
759
|
-
|
|
760
|
-
const run = async () => {
|
|
761
|
-
// Prime the visible unix first so the active frame hits GPU before background slots.
|
|
762
|
-
const curUx =
|
|
763
|
-
snapshot.nexradTimestamp != null && Number.isFinite(Number(snapshot.nexradTimestamp))
|
|
764
|
-
? Number(snapshot.nexradTimestamp)
|
|
765
|
-
: NaN;
|
|
766
|
-
if (Number.isFinite(curUx)) {
|
|
767
|
-
const p0 = this._buildFetchParamsForUnix(snapshot, curUx);
|
|
768
|
-
if (p0) {
|
|
769
|
-
const fr0 = this._frameCache.get(p0.fetchKey);
|
|
770
|
-
if (fr0) {
|
|
771
|
-
this._primeNativeGpuUploadIfNeeded(snapshot, curUx, p0, fr0, signal);
|
|
772
|
-
await yieldToJsEventLoop();
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
let cursor = 0;
|
|
778
|
-
const work = async () => {
|
|
779
|
-
while (cursor < times.length && !signal.aborted) {
|
|
780
|
-
const i = cursor++;
|
|
781
|
-
const unix = times[i];
|
|
782
|
-
const slot = nexradPerfSpan(`preload.slot idx=${i + 1}/${times.length} unix=${unix}`);
|
|
783
|
-
const p = this._buildFetchParamsForUnix(snapshot, unix);
|
|
784
|
-
if (!p || signal.aborted) {
|
|
785
|
-
slot.end({ outcome: 'noParamsOrAborted' });
|
|
786
|
-
} else if (this._frameCache.has(p.fetchKey)) {
|
|
787
|
-
preloadSkippedCached++;
|
|
788
|
-
const cachedFrame = this._frameCache.get(p.fetchKey);
|
|
789
|
-
this._primeNativeGpuUploadIfNeeded(snapshot, unix, p, cachedFrame, signal);
|
|
790
|
-
await yieldToJsEventLoop();
|
|
791
|
-
slot.end({ outcome: 'alreadyCached', fetchKeyTail: p.fetchKey.slice(-64) });
|
|
792
|
-
} else {
|
|
793
|
-
try {
|
|
794
|
-
const frame = await this._fetchFrame(snapshot, unix, {
|
|
795
|
-
signal,
|
|
796
|
-
priority: 'prefetch',
|
|
797
|
-
});
|
|
798
|
-
if (frame && !signal.aborted) {
|
|
799
|
-
this._frameCache.set(p.fetchKey, frame);
|
|
800
|
-
preloadCompleted++;
|
|
801
|
-
this._primeNativeGpuUploadIfNeeded(snapshot, unix, p, frame, signal);
|
|
802
|
-
await yieldToJsEventLoop();
|
|
803
|
-
slot.end({
|
|
804
|
-
outcome: 'cachedFrame',
|
|
805
|
-
fetchKeyTail: p.fetchKey.slice(-64),
|
|
806
|
-
cacheSizeAfter: this._frameCache.size,
|
|
807
|
-
});
|
|
808
|
-
} else {
|
|
809
|
-
slot.end({ outcome: 'noFrame', aborted: signal.aborted });
|
|
810
|
-
}
|
|
811
|
-
} catch (err) {
|
|
812
|
-
preloadFailed++;
|
|
813
|
-
slot.end({ outcome: 'error', message: err?.message || String(err) });
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
await yieldToJsEventLoop();
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
await Promise.all(Array.from({ length: PRELOAD_CONCURRENCY }, () => work()));
|
|
820
|
-
|
|
821
|
-
// Catch-up: any unix still missing a GPU slot (e.g. layer mounted mid-preload) gets one upload pass.
|
|
822
|
-
if (!signal.aborted) {
|
|
823
|
-
for (let i = 0; i < times.length && !signal.aborted; i++) {
|
|
824
|
-
const unix = times[i];
|
|
825
|
-
const p = this._buildFetchParamsForUnix(snapshot, unix);
|
|
826
|
-
if (!p) continue;
|
|
827
|
-
const frame = this._frameCache.get(p.fetchKey);
|
|
828
|
-
if (!frame) continue;
|
|
829
|
-
const syncKey = this._syncIdentity(snapshot, p, unix);
|
|
830
|
-
if (this._nativeGpuReadyKeys.has(syncKey)) continue;
|
|
831
|
-
this._primeNativeGpuUploadIfNeeded(snapshot, unix, p, frame, signal);
|
|
832
|
-
await yieldToJsEventLoop();
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
preloadBatch.end({
|
|
837
|
-
outcome: signal.aborted ? 'aborted' : 'complete',
|
|
838
|
-
completed: preloadCompleted,
|
|
839
|
-
failed: preloadFailed,
|
|
840
|
-
skippedCached: preloadSkippedCached,
|
|
841
|
-
finalCacheSize: this._frameCache.size,
|
|
842
|
-
gpuReadyCount: this._nativeGpuReadyKeys.size,
|
|
843
|
-
});
|
|
844
|
-
};
|
|
845
|
-
|
|
846
|
-
void run();
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
async sync(state) {
|
|
850
|
-
const unixEarly = state.nexradTimestamp != null ? Number(state.nexradTimestamp) : NaN;
|
|
851
|
-
const syncTotal = nexradPerfSpan(
|
|
852
|
-
`sync.total site=${state.nexradSite} unix=${unixEarly} product=${state.nexradProduct} source=${state.nexradDataSource} tilt=${state.nexradTilt}`,
|
|
853
|
-
);
|
|
854
|
-
|
|
855
|
-
if (!state.isNexrad || !state.nexradSite || state.nexradTimestamp == null) {
|
|
856
|
-
this.destroy();
|
|
857
|
-
syncTotal.end({ outcome: 'skippedNotNexradOrMissing' });
|
|
858
|
-
return;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (!ensureRadarArchiveBindings('sync')) {
|
|
862
|
-
syncTotal.end({ outcome: 'skippedMissingArchiveBindings' });
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
const tAuth = nexradPerfSpan('sync.setArchiveAuth');
|
|
867
|
-
setNexradArchiveApiKey(this.core.apiKey || '');
|
|
868
|
-
setNexradArchiveBundleId(this.core.bundleId || '');
|
|
869
|
-
setNexradArchiveSiteOrigin(this.core.gridRequestSiteOrigin || 'https://localhost');
|
|
870
|
-
setNexradSitesFetchAuth(this.core.apiKey || '', this.core.bundleId || '');
|
|
871
|
-
tAuth.end({});
|
|
872
|
-
|
|
873
|
-
const unix = Number(state.nexradTimestamp);
|
|
874
|
-
|
|
875
|
-
const tParams = nexradPerfSpan('sync.buildFetchParams');
|
|
876
|
-
const p = this._buildFetchParamsForUnix(state, unix);
|
|
877
|
-
tParams.end({ ok: !!p });
|
|
878
|
-
if (!p) {
|
|
879
|
-
const ctx = this._resolveListingContext(state);
|
|
880
|
-
syncTotal.end({ outcome: 'noFetchParams' });
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const tIdentity = nexradPerfSpan('sync.syncIdentity');
|
|
885
|
-
const syncKey = this._syncIdentity(state, p, unix);
|
|
886
|
-
tIdentity.end({ syncKeyTail: syncKey.slice(-96), lastSyncKeyMatch: syncKey === this._lastSyncKey });
|
|
887
|
-
|
|
888
|
-
const jsCachedForShortCircuit = this._frameCache.get(p.fetchKey);
|
|
889
|
-
if (syncKey === this._lastSyncKey && this._nativeFrameUploaded && jsCachedForShortCircuit) {
|
|
890
|
-
const tStyle = nexradPerfSpan('sync.shortCircuit_styleOnly');
|
|
891
|
-
this.applyStyleFromState(state);
|
|
892
|
-
tStyle.end({});
|
|
893
|
-
syncTotal.end({ outcome: 'styleOnlyShortCircuit' });
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
const tCacheLookup = nexradPerfSpan('sync.memoryCacheLookup');
|
|
898
|
-
const cached = this._frameCache.get(p.fetchKey);
|
|
899
|
-
tCacheLookup.end({ hit: !!cached, cacheSize: this._frameCache.size, fetchKeyTail: p.fetchKey.slice(-80) });
|
|
900
|
-
|
|
901
|
-
const tGpuTry = nexradPerfSpan('sync.tryGpuCacheActivate');
|
|
902
|
-
if (cached && this._nativeGpuReadyKeys.has(syncKey)) {
|
|
903
|
-
const n = this._native();
|
|
904
|
-
if (n?.activateNexradCachedFrame) {
|
|
905
|
-
const tAct = nexradPerfSpan('sync.dispatchActivateCachedFrame');
|
|
906
|
-
n.activateNexradCachedFrame(syncKey);
|
|
907
|
-
tAct.end({ keyLen: syncKey.length });
|
|
908
|
-
const tStyle = nexradPerfSpan('sync.applyStyleAfterGpuActivate');
|
|
909
|
-
this.applyStyleFromState(state);
|
|
910
|
-
tStyle.end({});
|
|
911
|
-
const meta = this._uploadMetaBySyncKey.get(syncKey);
|
|
912
|
-
if (meta) {
|
|
913
|
-
this._lastUploadMeta = { valueScale: meta.valueScale, valueOffset: meta.valueOffset };
|
|
914
|
-
}
|
|
915
|
-
this._lastSyncKey = syncKey;
|
|
916
|
-
this._nativeFrameUploaded = true;
|
|
917
|
-
tGpuTry.end({ outcome: 'gpuCacheActivate', gpuReadySetSize: this._nativeGpuReadyKeys.size });
|
|
918
|
-
syncTotal.end({ outcome: 'gpuCacheActivate' });
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
tGpuTry.end({
|
|
923
|
-
outcome: 'noGpuActivate',
|
|
924
|
-
hasJsCachedFrame: !!cached,
|
|
925
|
-
gpuReadyForSyncKey: this._nativeGpuReadyKeys.has(syncKey),
|
|
926
|
-
hasActivateMethod: !!this._native()?.activateNexradCachedFrame,
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
if (cached) {
|
|
930
|
-
const tUpload = nexradPerfSpan('sync.uploadFromMemoryCache_toNative');
|
|
931
|
-
this._uploadFrameToNative(state, cached, p, unix);
|
|
932
|
-
tUpload.end({});
|
|
933
|
-
this._lastSyncKey = syncKey;
|
|
934
|
-
syncTotal.end({ outcome: 'memoryCacheHit' });
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
this._abort?.abort();
|
|
939
|
-
this._abort = new AbortController();
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
let frame;
|
|
943
|
-
try {
|
|
944
|
-
frame = await this._fetchFrame(state, unix, {
|
|
945
|
-
signal: this._abort.signal,
|
|
946
|
-
priority: 'display',
|
|
947
|
-
});
|
|
948
|
-
} catch (err) {
|
|
949
|
-
syncTotal.end({ outcome: 'fetchThrew', message: err?.message || String(err) });
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
if (!frame || this._abort.signal.aborted) {
|
|
954
|
-
syncTotal.end({
|
|
955
|
-
outcome: 'fetchNoFrame',
|
|
956
|
-
aborted: this._abort.signal.aborted,
|
|
957
|
-
hadFrame: !!frame,
|
|
958
|
-
});
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
const tPut = nexradPerfSpan('sync.frameCacheSet');
|
|
963
|
-
this._frameCache.set(p.fetchKey, frame);
|
|
964
|
-
tPut.end({ cacheSizeAfter: this._frameCache.size });
|
|
965
|
-
|
|
966
|
-
const tUploadFetched = nexradPerfSpan('sync.uploadFetchedFrame_toNative');
|
|
967
|
-
this._uploadFrameToNative(state, frame, p, unix);
|
|
968
|
-
tUploadFetched.end({});
|
|
969
|
-
this._lastSyncKey = syncKey;
|
|
970
|
-
syncTotal.end({ outcome: 'fetchedAndUploaded' });
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
getInspectPayload(lng, lat, state) {
|
|
974
|
-
if (!state?.isNexrad || !state.nexradSite || state.nexradTimestamp == null) {
|
|
975
|
-
return null;
|
|
976
|
-
}
|
|
977
|
-
if (state.visible === false || (state.opacity ?? 1) <= 0) {
|
|
978
|
-
return null;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
const unix = Number(state.nexradTimestamp);
|
|
982
|
-
const p = this._buildFetchParamsForUnix(state, unix);
|
|
983
|
-
if (!p) {
|
|
984
|
-
return null;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
const frame = this._frameCache.get(p.fetchKey);
|
|
988
|
-
if (!frame) {
|
|
989
|
-
return null;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
const colormapFlat = state.colormap;
|
|
993
|
-
if (!colormapFlat || colormapFlat.length < 2) {
|
|
994
|
-
return null;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
const rangeMin = colormapFlat[0];
|
|
998
|
-
const rangeMax = colormapFlat[colormapFlat.length - 2];
|
|
999
|
-
|
|
1000
|
-
const radarVariable = (state.nexradProduct || 'REF').toUpperCase();
|
|
1001
|
-
const radarSource = state.nexradDataSource === 'level3' ? 'level3' : 'level2';
|
|
1002
|
-
const displayUnits =
|
|
1003
|
-
state.units && String(state.units).toLowerCase() !== 'none'
|
|
1004
|
-
? state.units
|
|
1005
|
-
: isVelocityStyleRadarVar(radarVariable)
|
|
1006
|
-
? 'm/s'
|
|
1007
|
-
: '';
|
|
1008
|
-
|
|
1009
|
-
const uploadOptsForReadout = mapboxFrameUploadOptionsForNexradState(state);
|
|
1010
|
-
const gpuFrame = this._getPreparedReadoutFrame(frame, uploadOptsForReadout);
|
|
1011
|
-
const sample = sampleNexradFrameAtLatLon(gpuFrame, lat, lng, { smoothPolar: this._gateSmoothing });
|
|
1012
|
-
if (!sample) {
|
|
1013
|
-
return null;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
const valueMs = sample.value;
|
|
1017
|
-
const isHydro = radarSource === 'level3' && nexradLevel3IsHydrometeorClassification(radarVariable);
|
|
1018
|
-
|
|
1019
|
-
let valueOut;
|
|
1020
|
-
let unitOut = displayUnits || '';
|
|
1021
|
-
|
|
1022
|
-
if (isVelocityStyleRadarVar(radarVariable)) {
|
|
1023
|
-
valueOut = velocityMsToDisplay(valueMs, displayUnits);
|
|
1024
|
-
} else if (isHydro) {
|
|
1025
|
-
const label = nexradHydrometeorLabelForClassIndex(valueMs);
|
|
1026
|
-
if (label == null) {
|
|
1027
|
-
return null;
|
|
1028
|
-
}
|
|
1029
|
-
valueOut = label;
|
|
1030
|
-
unitOut = '';
|
|
1031
|
-
} else {
|
|
1032
|
-
valueOut = formatNexradInspectNumeric(valueMs, radarVariable);
|
|
1033
|
-
const bu = state.colormapBaseUnit;
|
|
1034
|
-
if (bu != null && String(bu).length > 0 && String(bu).toLowerCase() !== 'none') {
|
|
1035
|
-
unitOut = bu;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
let inRange;
|
|
1040
|
-
if (isHydro) {
|
|
1041
|
-
const idx = Math.round(Number(valueMs));
|
|
1042
|
-
inRange =
|
|
1043
|
-
(rangeMin == null || idx >= rangeMin) && (rangeMax == null || idx <= rangeMax);
|
|
1044
|
-
} else if (isVelocityStyleRadarVar(radarVariable)) {
|
|
1045
|
-
inRange =
|
|
1046
|
-
(rangeMin == null || valueOut >= rangeMin) && (rangeMax == null || valueOut <= rangeMax);
|
|
1047
|
-
} else {
|
|
1048
|
-
// Do not apply the colormap *lower* stop as a hard readout floor: weak echoes below the first
|
|
1049
|
-
// legend tick can still be drawn (LUT / GL), and users expect a number there.
|
|
1050
|
-
inRange = rangeMax == null || valueOut <= rangeMax;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
if (!inRange) {
|
|
1054
|
-
return null;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
const tiltDeg = Number.isFinite(state.nexradTilt) ? state.nexradTilt : getDefaultRadarTilt(state.nexradSite);
|
|
1058
|
-
const bhKm = beamHeightKm(sample.groundRangeKm, tiltDeg);
|
|
1059
|
-
const metric = state.units === 'metric';
|
|
1060
|
-
const beamHeightDisplay = metric ? bhKm : bhKm * 3.280839895013123;
|
|
1061
|
-
const beamUnit = metric ? 'km' : 'kft';
|
|
1062
|
-
|
|
1063
|
-
const fldKey = state.variable;
|
|
1064
|
-
return {
|
|
1065
|
-
lngLat: { lng, lat },
|
|
1066
|
-
variable: {
|
|
1067
|
-
code: fldKey,
|
|
1068
|
-
name: this.core.getVariableDisplayName(fldKey),
|
|
1069
|
-
},
|
|
1070
|
-
value: valueOut,
|
|
1071
|
-
unit: unitOut,
|
|
1072
|
-
beamHeight:
|
|
1073
|
-
Number.isFinite(beamHeightDisplay) && Number.isFinite(bhKm)
|
|
1074
|
-
? { value: beamHeightDisplay, unit: beamUnit }
|
|
1075
|
-
: null,
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1
|
+
import{getUnitConversionFunction as O,getDefaultRadarTilt as Q,nexradBinGroupIdForKey as X,variableToNexradGroup as ee}from"./nexradSdkImports.js";import{fetchAndApplyLevel2ListingToCore as te,isValidLevel2SweepObjectKey as j,level2ListingCacheNeedsRefresh as D,level2ListingEmptyHint as ae,listingContextParams as E,rebuildLevel2TimeToKeyMap as re,resolveLevel2ObjectKeyForFetch as ie}from"./nexradLevel2Keys.js";import{fromByteArray as J}from"base64-js";import{fetchAndParseArchive as k,getNexradArchiveAuthSnapshot as ne,objectKeyToUrl as G,setNexradArchiveApiKey as L,setNexradArchiveBundleId as R,setNexradArchiveSiteOrigin as w,setNexradArchiveUserId as $,setNexradSitesJsonUrl as se,setNexradSitesFetchAuth as B}from"./radarArchiveCore.bundled.js";import{RN_DEFAULT_GRID_REQUEST_SITE_ORIGIN as I}from"../gridCdnAuth";import{prepareRadarFrameForGpuReadout as oe}from"./radarFrameGpuMatch.bundled.js";import{mapboxFrameUploadOptionsForNexradState as V}from"./nexradMapboxFrameOpts.bundled.js";import{sampleNexradFrameAtLatLon as le}from"./nexradCrossSectionSampleAtLatLon.bundled.js";import{buildNexradLutRgba as de}from"./nexradLutBuild.js";import{Platform as ce}from"react-native";import{nexradDiagBootSnapshot as ue,nexradDiagGateTextureSummary as he,nexradPerfSpan as h,nexradPipelineLog as y}from"./nexradDiag.js";import{aguaceroDebug as z,getAguaceroAuthDiagnosticSnapshot as H,isAguaceroRnDebugEnabled as W}from"../aguaceroRnDebug";ue({archiveExports:{setNexradArchiveApiKey:typeof L,setNexradArchiveBundleId:typeof R,objectKeyToUrl:typeof G,fetchAndParseArchive:typeof k}});function q(c){return typeof L=="function"&&typeof R=="function"&&typeof G=="function"&&typeof k=="function"}function me(c,e,t=600){const a=e[String(c)];if(a)return a;const n=Object.entries(e||{});if(n.length===0)return null;let i=null,r=1/0;for(const[s,l]of n){const o=Number(s);if(!Number.isFinite(o))continue;const m=Math.abs(o-c);m<r&&(r=m,i=l)}return i==null||r>t&&Number.isFinite(t)?null:i}function U(c){return c==="VEL"||c==="SW"||c==="N0G"||c==="N0W"}function pe(c,e,t){if(c==null||e==null||!t||t.toLowerCase()==="m/s"||t.toLowerCase()==="ms")return[c,e];const a=O(t,"m/s","nexrad_vel");return a?[a(c),a(e)]:[c,e]}function ye(c,e,t){if(!c||!Array.isArray(c)||c.length<2||!U(e)||!t||t.toLowerCase()==="m/s"||t.toLowerCase()==="ms")return c;const a=O(t,"m/s",e==="SW"||e==="N0W"?"nexrad_sw":"nexrad_vel");if(!a)return c;const n=[...c];for(let i=0;i<n.length;i+=2){const r=n[i];typeof r=="number"&&Number.isFinite(r)&&(n[i]=a(r))}return n}function ge(c,e,t,a,n){const i=(t||"").toUpperCase();if(U(i)){const[r,s]=pe(c,e,a);return[r??0,s??80]}if(n==="level3"&&(i==="N0H"||i==="HHC")){const r=c??0;let s=e??11;return s>11&&(s=11),s<r&&(s=r),[r,s]}if(i==="KDP"){const r=c??-2;let s=e??8;return s<r&&(s=r),[r,s]}return[c??0,e??80]}function fe(c){const e=(c||"").toUpperCase();return e==="N0H"||e==="HHC"}const Y=["Biological","Ground clutter","Ice crystals","Dry snow","Wet snow","Light rain","Heavy rain","Big drops","Graupel","Hail/rain","Large hail","Giant hail"];function xe(c){const e=Math.round(Number(c));return e>=12?null:e>=0&&e<Y.length?Y[e]:`Class ${e}`}function ve(c,e){if(!e||e.toLowerCase()==="m/s"||e.toLowerCase()==="ms")return c;const t=O("m/s",e,"nexrad_vel");return t?t(c):c}function _e(c,e){const t=e*Math.PI/180,a=6371e3*4/(3*1e3),n=Math.max(Math.cos(t),.05),i=c/n;return Math.sqrt(i*i+a*a+2*i*a*Math.sin(t))-a}function Se(c,e){const t=(e||"").toUpperCase(),a=e||"";return t.includes("RATE")||a.toLowerCase().includes("rate")?Number(c.toFixed(3).replace(/\.?0+$/,"")):t==="RHO"||t==="ZDR"||a.includes("RhoHV")||a.includes("Zdr")?Number(c.toFixed(2)):Math.abs(c)<10?Number(c.toFixed(1)):Math.round(c)}const be=ce.OS==="ios"?2:1;function M(){return new Promise(c=>setTimeout(c,0))}const Ne=12;function Te(c){const e=c.length,t=new Uint8Array(e*2);let a=0,n=0;for(;n<e;){let i=c[n],r=c[n+1],s=1,l=e-n>>1;l>65535&&(l=65535);let o=n+2;for(;s<l&&c[o]===i&&c[o+1]===r;)s++,o+=2;t[a++]=s&255,t[a++]=s>>8&255,t[a++]=i,t[a++]=r,n=o}return t.subarray(0,a)}export class NexradAndroidController{constructor(e,t,a={}){this.core=e,this._layerRef=t,this._lastSyncKey=null,this._abort=null,this._interpolateColormap=a.interpolateNexradColormap!==!1,this._gateSmoothing=a.nexradGateSmoothing===!0,this._frameCache=new Map,this._gpuReadoutPrep=new WeakMap,this._preloadAbort=null,this._preloadTimelineSig=null,this._listingRefreshPromise=null,this._nativeFrameUploaded=!1,this._lastUploadMeta=null,this._nativeUploadJsonLru=new Map,this._nativeGpuReadyKeys=new Set,this._uploadMetaBySyncKey=new Map,this._gpuReadyEventSub=null;try{const{DeviceEventEmitter:i}=require("react-native");i&&typeof i.addListener=="function"&&(this._gpuReadyEventSub=i.addListener("AguaceroNexradGpuFrameReady",r=>{const s=r&&r.nativeGpuCacheKey;typeof s=="string"&&s.length>0&&(this._nativeGpuReadyKeys.add(s),this._trimNativeGpuReadyKeys(96))}))}catch{}const n=typeof e?.baseGridUrl=="string"?e.baseGridUrl.replace(/\/$/,""):"";n&&typeof navigator<"u"&&navigator.product==="ReactNative"&&(se(`${n}/data/nexrad.json`),B(e.apiKey||"",e.bundleId||"")),L(e.apiKey||""),R(e.bundleId||""),w(e.gridRequestSiteOrigin||I),$(e.nexradUserId||"sdk-user"),W()&&z("nexrad.authConfigured",{...ne({phase:"NexradAndroidController.constructor"}),...H(e,{phase:"NexradAndroidController.constructor"})})}_trimNativeGpuReadyKeys(e){for(;this._nativeGpuReadyKeys.size>e;){const t=this._nativeGpuReadyKeys.values().next().value;if(t===void 0)break;this._nativeGpuReadyKeys.delete(t)}}_rememberUploadMetaForSyncKey(e,t,a){for(this._uploadMetaBySyncKey.set(e,{valueScale:t,valueOffset:a});this._uploadMetaBySyncKey.size>40;){const n=this._uploadMetaBySyncKey.keys().next().value;if(n===void 0)break;this._uploadMetaBySyncKey.delete(n)}}updateStyleOptions(e){e.interpolateNexradColormap!=null&&(this._interpolateColormap=e.interpolateNexradColormap!==!1),e.nexradGateSmoothing!=null&&(this._gateSmoothing=e.nexradGateSmoothing===!0)}destroy(){try{this._gpuReadyEventSub?.remove()}catch{}this._gpuReadyEventSub=null,this._nativeGpuReadyKeys.clear(),this._uploadMetaBySyncKey.clear(),this._abort?.abort(),this._abort=null,this._preloadAbort?.abort(),this._preloadAbort=null,this._frameCache.clear(),this._gpuReadoutPrep=new WeakMap,this._preloadTimelineSig=null,this._lastSyncKey=null,this._nativeFrameUploaded=!1,this._lastUploadMeta=null,this._nativeUploadJsonLru.clear(),this._layerRef?.current?.clearNexrad?.()}_getPreparedReadoutFrame(e,t){let a=this._gpuReadoutPrep.get(e);a||(a=new Map,this._gpuReadoutPrep.set(e,a));const n=t&&typeof t=="object"?JSON.stringify(t):String(t);let i=a.get(n);return i||(i=oe(e,t),a.set(n,i)),i}_native(){return this._layerRef?.current}_lutPack(e){const t=(e.nexradProduct||"REF").toUpperCase(),a=e.colormap,n=ye(a,t,e.units),[i,r]=ge(a?.[0],a?.[a.length-2],t,e.units,e.nexradDataSource),{bytes:s,discreteIntegerLut:l}=de(n,i,r,this._interpolateColormap);return{lutB64:J(s),lutMin:i,lutMax:r,discreteIntegerLut:l}}_nativeUploadJsonLruTouch(e){const t=this._nativeUploadJsonLru.get(e);return t?(this._nativeUploadJsonLru.delete(e),this._nativeUploadJsonLru.set(e,t),t):null}_nativeUploadJsonLruPut(e,t){for(this._nativeUploadJsonLru.has(e)&&this._nativeUploadJsonLru.delete(e),this._nativeUploadJsonLru.set(e,t);this._nativeUploadJsonLru.size>Ne;){const a=this._nativeUploadJsonLru.keys().next().value;if(a===void 0)break;this._nativeUploadJsonLru.delete(a)}}_nativeUploadLruKey(e,t){return`${t}|o:${e.opacity??1}|g:${this._gateSmoothing?1:0}|i:${this._interpolateColormap?1:0}`}_primeNativeGpuUploadIfNeeded(e,t,a,n,i){if(!(!n||!a||i&&i.aborted))try{const r=e.nexradTimestamp!=null&&Number.isFinite(Number(e.nexradTimestamp))?Number(e.nexradTimestamp):NaN;if(Number.isFinite(r)&&Number(t)!==r)return;const s=this._syncIdentity(e,a,t);if(this._nativeGpuReadyKeys.has(s)||!this._native()?.uploadNexradFrame)return;this._uploadFrameToNative(e,n,a,t)}catch{}}_uploadFrameToNative(e,t,a,n){const i=h(`uploadFrame.total site=${e.nexradSite} unix=${n} var=${a?.radarVar??e.nexradProduct}`),r=this._native();if(!r?.uploadNexradFrame){y("upload.skip",{reason:"noNativeUploadMethod",site:e.nexradSite,unix:n,layerRefSet:!!this._layerRef?.current}),i.end({outcome:"skippedNoNative"});return}const s=this._syncIdentity(e,a,n),l=this._nativeUploadLruKey(e,s),o=this._nativeUploadJsonLruTouch(l);if(o){if(this._lastUploadMeta={valueScale:o.valueScale,valueOffset:o.valueOffset},this._rememberUploadMetaForSyncKey(s,o.valueScale,o.valueOffset),r.activateNexradCachedFrame&&this._nativeGpuReadyKeys.has(s)){const K=h("uploadFrame.nativeGpuActivateOnly");r.activateNexradCachedFrame(s),K.end({keyLen:s.length});const Z=h("uploadFrame.styleAfterGpuActivate");this.applyStyleFromState(e),Z.end({}),this._nativeFrameUploaded=!0,i.end({outcome:"gpuActivateOnly",jsonCharsSkipped:o.json.length});return}const A=h("uploadFrame.nativeJsonLru.bridgeDispatch");r.uploadNexradFrame(o.json),A.end({jsonChars:o.json.length,path:"jsonLruHit"}),this._nativeFrameUploaded=!0,i.end({outcome:"jsonLruHit",jsonChars:o.json.length});return}const m=h("uploadFrame.mapboxFrameUploadOptions"),u=V(e);m.end({});const d=t;this._lastUploadMeta={valueScale:d.valueScale,valueOffset:d.valueOffset};const N=h("uploadFrame.lutPack_buildNexradLutRgba"),p=this._lutPack(e);N.end({lutB64Chars:p.lutB64?.length??0,discreteIntegerLut:p.discreteIntegerLut,lutMin:p.lutMin,lutMax:p.lutMax});const F=h("uploadFrame.rayBoundariesToJsArray"),g=d.rayBoundariesDeg?.slice?Array.from(d.rayBoundariesDeg):[...d.rayBoundariesDeg||[]];F.end({len:g.length});const f=h("uploadFrame.gateData_toBase64"),_=Te(d.gateData),C=J(_);f.end({gateB64Chars:C.length,rleBytes:_.length,rawBytes:d.gateData.length});const x={gateB64:C,gateIsRle:!0,nGates:d.nGates,nRays:d.nRays,stationLat:d.stationLat,stationLon:d.stationLon,firstGateKm:d.firstGateKm,gateWidthKm:d.gateWidthKm,rayBoundaries:g,lutB64:p.lutB64,valueScale:d.valueScale,valueOffset:d.valueOffset,lutMin:p.lutMin,lutMax:p.lutMax,discreteIntegerLut:p.discreteIntegerLut?1:0,opacity:e.opacity??1,gateSmoothPolar:this._gateSmoothing?1:0,interpolateLut:this._interpolateColormap?1:0,nativeGpuCacheKey:s,uploadOpts:u},S=h("uploadFrame.JSON_stringify_payload"),v=JSON.stringify(x);S.end({jsonChars:v.length,rayBoundaryLen:g.length});const b=h("uploadFrame.nativeUploadJsonLruPut");this._nativeUploadJsonLruPut(l,{json:v,valueScale:d.valueScale,valueOffset:d.valueOffset}),b.end({lruKeyTail:l.slice(-96),lruSize:this._nativeUploadJsonLru.size});const T=he(d.gateData,d.nGates,d.nRays,d.valueScale,d.valueOffset,p.lutMin,p.lutMax),P=h("uploadFrame.nativeBridge_uploadNexradFrame");r.uploadNexradFrame(v),P.end({jsonChars:v.length,path:"fullRebuild"}),this._rememberUploadMetaForSyncKey(s,d.valueScale,d.valueOffset),this._nativeFrameUploaded=!0,i.end({outcome:"fullPipeline",jsonChars:v.length,nGates:d.nGates,nRays:d.nRays})}applyStyleFromState(e){const t=h(`applyStyleFromState site=${e.nexradSite} product=${e.nexradProduct} tilt=${e.nexradTilt}`),a=this._native();if(!this._nativeFrameUploaded||!this._lastUploadMeta||!a?.uploadNexradStyleOnly){t.end({outcome:"skipped",nativeFrameUploaded:this._nativeFrameUploaded,hasUploadMeta:!!this._lastUploadMeta,hasStyleMethod:!!a?.uploadNexradStyleOnly});return}const n=h("applyStyleFromState.lutPack"),i=this._lutPack(e);n.end({lutB64Chars:i.lutB64?.length??0});const r={lutB64:i.lutB64,valueScale:this._lastUploadMeta.valueScale,valueOffset:this._lastUploadMeta.valueOffset,lutMin:i.lutMin,lutMax:i.lutMax,discreteIntegerLut:i.discreteIntegerLut?1:0,opacity:e.opacity??1,gateSmoothPolar:this._gateSmoothing?1:0,interpolateLut:this._interpolateColormap?1:0},s=h("applyStyleFromState.JSON_stringify"),l=JSON.stringify(r);s.end({jsonChars:l.length});const o=h("applyStyleFromState.nativeBridge");a.uploadNexradStyleOnly(l),o.end({jsonChars:l.length}),t.end({outcome:"dispatched"})}_resolveListingContext(e){const{site:t,elevNorm:a,group:n}=E(e),i=(e?.nexradDataSource||"level2")!=="level3"&&t?`${t}_${n}_${a}`:this.core._nexradTimesCacheKey?.(),r=i?this.core.nexradTimesByStation?.[i]:null,s=e?.nexradTimeToKeyMap&&typeof e.nexradTimeToKeyMap=="object"?e.nexradTimeToKeyMap:{};let l=r?.timeToKeyMap&&typeof r.timeToKeyMap=="object"?{...s,...r.timeToKeyMap}:{...s};const o=r?.level2Clens,m=Array.isArray(r?.unixTimes)&&r.unixTimes.length>0?r.unixTimes:Array.isArray(e?.availableNexradTimestamps)&&e.availableNexradTimestamps.length>0?e.availableNexradTimestamps:void 0;if(this._nexradRadarSource(e)==="level2"&&o&&typeof o=="object"&&Object.keys(o).length>0){const d=re({stationId:t,elevNorm:a,group:n,timeToKeyMap:l,level2Clens:o,unixTimes:m});Object.keys(d).length>0?l=d:Object.keys(l).length>0&&(l=Object.fromEntries(Object.entries(l).filter(([,N])=>j(N))))}else this._nexradRadarSource(e)==="level2"&&(l=Object.fromEntries(Object.entries(l).filter(([,d])=>j(d))));const u=r?.level3MotionTimeToKeyMap||{};return{nk:i,ent:r,timeToKeyMap:l,motionMap:u}}_nexradStateWithCoreListing(e){const{site:t,elevNorm:a,group:n}=E(e),i=(e?.nexradDataSource||"level2")!=="level3"&&t?`${t}_${n}_${a}`:this.core._nexradTimesCacheKey?.(),r=i?this.core.nexradTimesByStation?.[i]:null;if(!r)return e;const s=Array.isArray(r.unixTimes)?r.unixTimes:[],{timeToKeyMap:l}=this._resolveListingContext(e),o=this.core._getFilteredNexradTimestampsForVariable(s),m=this._nexradRadarSource(e)==="level2"?o.filter(d=>j(l[String(d)])):o.filter(d=>typeof l[String(d)]=="string"&&l[String(d)].length>0);let u=e.nexradTimestamp!=null?e.nexradTimestamp:this.core.state.nexradTimestamp!=null?this.core.state.nexradTimestamp:null;return u==null&&m.length>0&&(u=m[m.length-1]),{...e,availableNexradTimestamps:m,nexradTimeToKeyMap:l,nexradLevel3MotionTimeToKeyMap:r.level3MotionTimeToKeyMap||{},nexradTimestamp:u}}async _ensureLevel2ListingReady(e){if(this._nexradRadarSource(e)!=="level2")return;if(this._listingRefreshPromise){await this._listingRefreshPromise;return}const t=()=>this.core._nexradTimesCacheKey?.(),a=()=>t()?this.core.nexradTimesByStation?.[t()]:null;D(a())&&(this._listingRefreshPromise=(async()=>{try{await this.core.refreshNexradTimes();let n=a();if(D(n)){const s=await te(this.core,this.core.state,{siteOrigin:this.core.gridRequestSiteOrigin||""});if(n=a(),!s){y("listing.patchFailed",{site:e.nexradSite,cacheKey:t()});return}if(n?.level2ListingEmpty){y("listing.empty",{site:e.nexradSite,cacheKey:t(),hint:ae(e.nexradSite)});return}}const i=n?.timeToKeyMap||{},r=Object.values(i).find(s=>typeof s=="string"&&s.length>0);y("listing.refreshed",{site:e.nexradSite,product:e.nexradProduct,tilt:e.nexradTilt,cacheKey:t(),clensEntries:Object.keys(n?.level2Clens||{}).length,mappedTimes:Object.keys(i).length,sampleKeyTail:typeof r=="string"?r.slice(-72):r})}catch(n){y("listing.refreshFailed",{site:e.nexradSite,message:n?.message||String(n)})}finally{this._listingRefreshPromise=null}})(),await this._listingRefreshPromise)}_logListingDiag(e,t,a,n={}){const{nk:i,ent:r,timeToKeyMap:s,motionMap:l}=this._resolveListingContext(t),o=Object.keys(s||{}),m=a!=null?s[String(a)]:void 0;y(e,{site:t.nexradSite,product:t.nexradProduct,tilt:t.nexradTilt,radarSource:this._nexradRadarSource(t),unix:a,listingCacheKey:i,coreListingPresent:!!r,stateMapEntries:Object.keys(t.nexradTimeToKeyMap||{}).length,mergedMapEntries:o.length,motionMapEntries:Object.keys(l||{}).length,availableUnixCount:Array.isArray(t.availableNexradTimestamps)?t.availableNexradTimestamps.length:0,hasObjectKeyForUnix:a!=null&&Object.prototype.hasOwnProperty.call(s,String(a)),objectKeyTail:typeof m=="string"?m.slice(-72):m,sampleMapEntries:o.slice(0,4).map(u=>({unix:u,keyTail:String(s[u]||"").slice(-56)})),nativeLayerReady:!!this._native()?.uploadNexradFrame,...n})}_nexradRadarSource(e){return e.nexradDataSource==="level3"?"level3":"level2"}_buildFetchParamsForUnix(e,t,a={}){const n=a.logDiag!==!1,i=(e.nexradProduct||"REF").toUpperCase(),r=this._nexradRadarSource(e),s=X(ee(i)),{ent:l,timeToKeyMap:o,motionMap:m}=this._resolveListingContext(e),u=o[String(t)];if(!u)return n&&this._logListingDiag("fetchParams.missingObjectKey",e,t,{radarVar:i}),null;const d=r==="level2"?ie({state:e,listingEntry:l,unix:t,radarVar:i,rawKey:u}):u;if(!d)return n&&y("fetchParams.invalidLevel2ObjectKey",{site:e.nexradSite,unix:t,rawKeyTail:String(u).slice(-72),hasLevel2Clens:!!(l?.level2Clens&&Object.keys(l.level2Clens).length)}),null;let N=null;i==="VEL"&&e.nexradStormRelative&&(N=me(t,m));const p=G(d,r),F=`${p}|${i}|${r}|${N||""}`;return{objectKey:d,url:p,fetchKey:F,motionObjectKey:N,radarVar:i,radarSource:r,groupId:s}}async _fetchFrame(e,t,{signal:a,priority:n}){const i=h(`fetchAndParseArchive priority=${n} site=${e.nexradSite} unix=${t} product=${e.nexradProduct}`),r=this._buildFetchParamsForUnix(e,t);if(!r)return i.end({ok:!1,reason:"noFetchParams"}),null;y("fetch.start",{priority:n,site:e.nexradSite,unix:t,radarVar:r.radarVar,radarSource:r.radarSource,url:r.url?.replace(/([?&])apiKey=[^&]*/gi,"$1apiKey=(redacted)"),objectKeyTail:(r.objectKey||"").slice(-72),motionKeyTail:(r.motionObjectKey||"").slice(-48)});try{const s=await k(r.url,r.objectKey,r.radarVar,r.groupId,r.radarSource,{signal:a,priority:n,level3MotionObjectKey:r.motionObjectKey}),l={ok:!!s,radarVar:r.radarVar,radarSource:r.radarSource,objectKeyTail:(r.objectKey||"").slice(-48),motionKeyTail:(r.motionObjectKey||"").slice(-48)};return s&&(l.nGates=s.nGates,l.nRays=s.nRays,l.gateBytes=s.gateData?.length??0),i.end(l),y(s?"fetch.ok":"fetch.emptyFrame",l),s}catch(s){const l={ok:!1,error:s?.message||String(s),radarVar:r.radarVar,radarSource:r.radarSource,objectKeyTail:(r.objectKey||"").slice(-48)};throw i.end(l),y("fetch.error",l),s}}_preloadTimelineSignature(e){const t=this.core._nexradTimesCacheKey?.()||"",a=[...e.availableNexradTimestamps||[]].map(Number).filter(i=>Number.isFinite(i)).sort((i,r)=>i-r).join(","),n=e.nexradTilt!=null&&Number.isFinite(Number(e.nexradTilt))?Number(e.nexradTilt).toFixed(3):"";return`${t}|${a}|sr:${e.nexradStormRelative?1:0}|tilt:${n}`}_syncIdentity(e,t,a){const n=e.nexradTilt!=null&&Number.isFinite(Number(e.nexradTilt))?Number(e.nexradTilt).toFixed(3):"";return`${t.fetchKey}|tilt:${n}|u:${a}`}async preloadAllAvailable(e){if(!e.isNexrad||!e.nexradSite){y("preload.skip",{reason:"notNexradOrNoSite",isNexrad:e.isNexrad,site:e.nexradSite});return}if(!q("preloadAllAvailable")){y("preload.skip",{reason:"missingArchiveBindings",site:e.nexradSite});return}let t=e;if([...t.availableNexradTimestamps||[]].length||(await this._ensureLevel2ListingReady(t),t=this._nexradStateWithCoreListing(t)),![...t.availableNexradTimestamps||[]].length){y("preload.skip",{reason:"noAvailableTimestamps",site:t.nexradSite,product:t.nexradProduct,hint:"Listing empty after refreshNexradTimes"});return}await this._ensureLevel2ListingReady(t),t=this._nexradStateWithCoreListing(t);const a=[...t.availableNexradTimestamps||[]].map(Number).filter(g=>Number.isFinite(g));if(!a.length){y("preload.skip",{reason:"noValidListingAfterRefresh",site:e.nexradSite,product:e.nexradProduct});return}const n=this._preloadTimelineSignature(t);if(n===this._preloadTimelineSig){y("preload.skip",{reason:"sameTimelineSig",site:e.nexradSite,times:a.length});return}this._preloadTimelineSig=n,this._preloadAbort?.abort(),this._preloadAbort=new AbortController;const i=this._preloadAbort.signal;let r=0,s=0;for(const g of a)this._buildFetchParamsForUnix(t,g,{logDiag:!1})?r++:s++;if(y("preload.start",{site:t.nexradSite,product:t.nexradProduct,dataSource:t.nexradDataSource,tilt:t.nexradTilt,times:a.length,validFetchParams:r,invalidFetchParams:s,displayUnix:t.nexradTimestamp,timelineSigTail:n.slice(-80)}),r===0){this._logListingDiag("preload.allFetchParamsInvalid",t,t.nexradTimestamp,{times:a.length});return}const l=t.nexradTimestamp!=null&&Number.isFinite(Number(t.nexradTimestamp))?Number(t.nexradTimestamp):NaN,o=new Set;for(const g of a){const f=this._buildFetchParamsForUnix(t,g);f&&o.add(f.fetchKey)}const m=Number.isFinite(l)?this._buildFetchParamsForUnix(t,l):null;m&&o.add(m.fetchKey);for(const g of this._frameCache.keys())o.has(g)||this._frameCache.delete(g);L(this.core.apiKey||""),R(this.core.bundleId||""),w(this.core.gridRequestSiteOrigin||I),$(this.core.nexradUserId||"sdk-user"),B(this.core.apiKey||"",this.core.bundleId||""),W()&&z("nexrad.authConfigured",H(this.core,{phase:"NexradAndroidController.preload",site:t.nexradSite,product:t.nexradProduct}));const u={...t},d=h(`preload.batch site=${t.nexradSite} times=${a.length}`);let N=0,p=0,F=0;await(async()=>{const g=u.nexradTimestamp!=null&&Number.isFinite(Number(u.nexradTimestamp))?Number(u.nexradTimestamp):NaN;if(Number.isFinite(g)){const x=this._buildFetchParamsForUnix(u,g);if(x){const S=this._frameCache.get(x.fetchKey);S&&(this._primeNativeGpuUploadIfNeeded(u,g,x,S,i),await M())}}let f=0;const _=async()=>{for(;f<a.length&&!i.aborted;){const x=f++,S=a[x],v=h(`preload.slot idx=${x+1}/${a.length} unix=${S}`),b=this._buildFetchParamsForUnix(u,S);if(!b||i.aborted)v.end({outcome:"noParamsOrAborted"});else if(this._frameCache.has(b.fetchKey)){F++;const T=this._frameCache.get(b.fetchKey);this._primeNativeGpuUploadIfNeeded(u,S,b,T,i),await M(),v.end({outcome:"alreadyCached",fetchKeyTail:b.fetchKey.slice(-64)})}else try{const T=await this._fetchFrame(u,S,{signal:i,priority:"prefetch"});T&&!i.aborted?(this._frameCache.set(b.fetchKey,T),N++,this._primeNativeGpuUploadIfNeeded(u,S,b,T,i),await M(),v.end({outcome:"cachedFrame",fetchKeyTail:b.fetchKey.slice(-64),cacheSizeAfter:this._frameCache.size})):v.end({outcome:"noFrame",aborted:i.aborted})}catch(T){p++,v.end({outcome:"error",message:T?.message||String(T)})}await M()}};if(await Promise.all(Array.from({length:be},()=>_())),!i.aborted)for(let x=0;x<a.length&&!i.aborted;x++){const S=a[x],v=this._buildFetchParamsForUnix(u,S);if(!v)continue;const b=this._frameCache.get(v.fetchKey);if(!b)continue;const T=this._syncIdentity(u,v,S);this._nativeGpuReadyKeys.has(T)||(this._primeNativeGpuUploadIfNeeded(u,S,v,b,i),await M())}const C={outcome:i.aborted?"aborted":"complete",completed:N,failed:p,skippedCached:F,finalCacheSize:this._frameCache.size,gpuReadyCount:this._nativeGpuReadyKeys.size};d.end(C),y("preload.done",C)})()}async sync(e){const t=e.nexradTimestamp!=null?Number(e.nexradTimestamp):NaN,a=h(`sync.total site=${e.nexradSite} unix=${t} product=${e.nexradProduct} source=${e.nexradDataSource} tilt=${e.nexradTilt}`);if(!e.isNexrad||!e.nexradSite){y("sync.skip",{reason:"notNexradOrNoSite",isNexrad:e.isNexrad,site:e.nexradSite}),this.destroy(),a.end({outcome:"skippedNotNexradOrNoSite"});return}if(!q("sync")){y("sync.skip",{reason:"missingArchiveBindings",site:e.nexradSite}),a.end({outcome:"skippedMissingArchiveBindings"});return}await this._ensureLevel2ListingReady(e),e=this._nexradStateWithCoreListing(e);let n=e.nexradTimestamp!=null?Number(e.nexradTimestamp):NaN;if(!Number.isFinite(n)&&this.core.state.nexradTimestamp!=null&&(n=Number(this.core.state.nexradTimestamp),e={...e,nexradTimestamp:n}),!Number.isFinite(n)&&Array.isArray(e.availableNexradTimestamps)&&e.availableNexradTimestamps.length>0&&(n=Number(e.availableNexradTimestamps[e.availableNexradTimestamps.length-1]),e={...e,nexradTimestamp:n},this.core.state.nexradTimestamp==null&&(this.core.state.nexradTimestamp=n,typeof this.core._emitStateChange=="function"&&this.core._emitStateChange())),!Number.isFinite(n)){y("sync.skip",{reason:"noTimestampYet",site:e.nexradSite,availableUnixCount:Array.isArray(e.availableNexradTimestamps)?e.availableNexradTimestamps.length:0}),a.end({outcome:"skippedNoTimestampYet"});return}y("sync.start",{site:e.nexradSite,product:e.nexradProduct,dataSource:e.nexradDataSource,tilt:e.nexradTilt,unix:e.nexradTimestamp,nativeLayerReady:!!this._native()?.uploadNexradFrame});const i=h("sync.setArchiveAuth");L(this.core.apiKey||""),R(this.core.bundleId||""),w(this.core.gridRequestSiteOrigin||I),$(this.core.nexradUserId||"sdk-user"),B(this.core.apiKey||"",this.core.bundleId||""),i.end({});const r=h("sync.buildFetchParams"),s=this._buildFetchParamsForUnix(e,n);if(r.end({ok:!!s}),!s){this._logListingDiag("sync.noFetchParams",e,n),a.end({outcome:"noFetchParams"}),y("sync.done",{outcome:"noFetchParams",site:e.nexradSite,unix:n});return}const l=h("sync.syncIdentity"),o=this._syncIdentity(e,s,n);l.end({syncKeyTail:o.slice(-96),lastSyncKeyMatch:o===this._lastSyncKey});const m=this._frameCache.get(s.fetchKey);if(o===this._lastSyncKey&&this._nativeFrameUploaded&&m){const f=h("sync.shortCircuit_styleOnly");this.applyStyleFromState(e),f.end({}),a.end({outcome:"styleOnlyShortCircuit"});return}const u=h("sync.memoryCacheLookup"),d=this._frameCache.get(s.fetchKey);u.end({hit:!!d,cacheSize:this._frameCache.size,fetchKeyTail:s.fetchKey.slice(-80)});const N=h("sync.tryGpuCacheActivate");if(d&&this._nativeGpuReadyKeys.has(o)){const f=this._native();if(f?.activateNexradCachedFrame){const _=h("sync.dispatchActivateCachedFrame");f.activateNexradCachedFrame(o),_.end({keyLen:o.length});const C=h("sync.applyStyleAfterGpuActivate");this.applyStyleFromState(e),C.end({});const x=this._uploadMetaBySyncKey.get(o);x&&(this._lastUploadMeta={valueScale:x.valueScale,valueOffset:x.valueOffset}),this._lastSyncKey=o,this._nativeFrameUploaded=!0,N.end({outcome:"gpuCacheActivate",gpuReadySetSize:this._nativeGpuReadyKeys.size}),a.end({outcome:"gpuCacheActivate"});return}}if(N.end({outcome:"noGpuActivate",hasJsCachedFrame:!!d,gpuReadyForSyncKey:this._nativeGpuReadyKeys.has(o),hasActivateMethod:!!this._native()?.activateNexradCachedFrame}),d){const f=h("sync.uploadFromMemoryCache_toNative");this._uploadFrameToNative(e,d,s,n),f.end({}),this._lastSyncKey=o,a.end({outcome:"memoryCacheHit"});return}this._abort?.abort(),this._abort=new AbortController;let p;try{p=await this._fetchFrame(e,n,{signal:this._abort.signal,priority:"display"})}catch(f){const _=f?.message||String(f);a.end({outcome:"fetchThrew",message:_}),y("sync.done",{outcome:"fetchThrew",site:e.nexradSite,unix:n,message:_});return}if(!p||this._abort.signal.aborted){const f={outcome:"fetchNoFrame",aborted:this._abort.signal.aborted,hadFrame:!!p};a.end(f),y("sync.done",{...f,site:e.nexradSite,unix:n});return}const F=h("sync.frameCacheSet");this._frameCache.set(s.fetchKey,p),F.end({cacheSizeAfter:this._frameCache.size});const g=h("sync.uploadFetchedFrame_toNative");this._uploadFrameToNative(e,p,s,n),g.end({}),this._lastSyncKey=o,a.end({outcome:"fetchedAndUploaded"}),y("sync.done",{outcome:"fetchedAndUploaded",site:e.nexradSite,unix:n,nGates:p.nGates,nRays:p.nRays,nativeFrameUploaded:this._nativeFrameUploaded})}getInspectPayload(e,t,a){if(!a?.isNexrad||!a.nexradSite||a.nexradTimestamp==null||a.visible===!1||(a.opacity??1)<=0)return null;const n=Number(a.nexradTimestamp),i=this._buildFetchParamsForUnix(a,n);if(!i)return null;const r=this._frameCache.get(i.fetchKey);if(!r)return null;const s=a.colormap;if(!s||s.length<2)return null;const l=s[0],o=s[s.length-2],m=(a.nexradProduct||"REF").toUpperCase(),u=this._nexradRadarSource(a)||"level3",d=a.units&&String(a.units).toLowerCase()!=="none"?a.units:U(m)?"m/s":"",N=V(a),p=this._getPreparedReadoutFrame(r,N),F=le(p,t,e,{smoothPolar:this._gateSmoothing});if(!F)return null;const g=F.value,f=u==="level3"&&fe(m);let _,C=d||"";if(U(m))_=ve(g,d);else if(f){const K=xe(g);if(K==null)return null;_=K,C=""}else{_=Se(g,m);const K=a.colormapBaseUnit;K!=null&&String(K).length>0&&String(K).toLowerCase()!=="none"&&(C=K)}let x;if(f){const K=Math.round(Number(g));x=(l==null||K>=l)&&(o==null||K<=o)}else U(m)?x=(l==null||_>=l)&&(o==null||_<=o):x=o==null||_<=o;if(!x)return null;const S=Number.isFinite(a.nexradTilt)?a.nexradTilt:Q(a.nexradSite),v=_e(F.groundRangeKm,S),b=a.units==="metric",T=b?v:v*3.280839895013123,P=b?"km":"kft",A=a.variable;return{lngLat:{lng:e,lat:t},variable:{code:A,name:this.core.getVariableDisplayName(A)},value:_,unit:C,beamHeight:Number.isFinite(T)&&Number.isFinite(v)?{value:T,unit:P}:null}}}
|