@aguacerowx/react-native 0.0.39 → 0.0.42

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.
Files changed (408) hide show
  1. package/README.md +126 -126
  2. package/aguacerowx-react-native.podspec +38 -38
  3. package/android/.gradle/buildOutputCleanup/cache.properties +2 -2
  4. package/android/build/.transforms/42e9b8fa82d77a1c205db5bf0d0ed519/results.bin +1 -0
  5. package/android/build/.transforms/42e9b8fa82d77a1c205db5bf0d0ed519/transformed/classes/classes_dex/classes.dex +0 -0
  6. package/android/build/.transforms/8f329a9571a96a1c1c0869d49784e448/results.bin +1 -0
  7. package/android/build/.transforms/8f329a9571a96a1c1c0869d49784e448/transformed/classes/classes_dex/classes.dex +0 -0
  8. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/results.bin +1 -0
  9. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/AguaceroPackage.dex +0 -0
  10. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/BuildConfig.dex +0 -0
  11. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.dex +0 -0
  12. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/GridRenderLayer.dex +0 -0
  13. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/GridRenderLayerView.dex +0 -0
  14. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/GridRenderManager.dex +0 -0
  15. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/InspectorModule.dex +0 -0
  16. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/ShaderUtils.dex +0 -0
  17. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.dex +0 -0
  18. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/aguacerowx/reactnative/WeatherFrameProcessorModule.dex +0 -0
  19. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.dex +0 -0
  20. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.dex +0 -0
  21. package/android/build/.transforms/c8ab78b63f2cc835ac936d58e29a17ab/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  22. package/android/build/.transforms/f95abdfc98a7a06fc247f75cdd74def9/results.bin +1 -0
  23. package/android/build/.transforms/f95abdfc98a7a06fc247f75cdd74def9/transformed/classes/classes_dex/classes.dex +0 -0
  24. package/android/build/generated/source/buildConfig/debug/com/aguacerowx/reactnative/BuildConfig.java +10 -0
  25. package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.java +43 -0
  26. package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.java +22 -0
  27. package/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec-generated.cpp +22 -0
  28. package/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec.h +24 -0
  29. package/android/build/generated/source/codegen/jni/CMakeLists.txt +28 -0
  30. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI-generated.cpp +17 -0
  31. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI.h +19 -0
  32. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.cpp +22 -0
  33. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.h +24 -0
  34. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.cpp +16 -0
  35. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.h +23 -0
  36. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.cpp +62 -0
  37. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.h +40 -0
  38. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.cpp +17 -0
  39. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.h +32 -0
  40. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.cpp +16 -0
  41. package/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.h +20 -0
  42. package/android/build/generated/source/codegen/schema.json +1 -0
  43. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +8 -0
  44. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  45. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  46. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  47. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  48. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  49. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +4 -0
  50. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_fragment_shader.glsl.flat +0 -0
  51. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_vertex_shader.glsl.flat +0 -0
  52. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_fragment_shader.glsl.flat +0 -0
  53. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_vertex_shader.glsl.flat +0 -0
  54. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +5 -0
  55. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  56. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  57. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  58. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  59. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  60. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  61. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  62. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  63. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  64. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  65. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  66. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  67. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  68. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  69. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  70. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  71. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +6 -0
  72. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +8 -0
  73. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +8 -0
  74. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  75. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  76. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_fragment_shader.glsl +13 -0
  77. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_vertex_shader.glsl +13 -0
  78. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/fragment_shader.glsl +181 -0
  79. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/vertex_shader.glsl +20 -0
  80. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  81. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  82. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  83. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  84. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  85. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  86. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  87. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  88. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  89. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  90. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  91. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  92. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  93. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +5 -0
  94. package/android/build/outputs/logs/manifest-merger-debug-report.txt +17 -0
  95. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/AguaceroPackage.class.uniqueId2 +0 -0
  96. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule$1.class.uniqueId0 +0 -0
  97. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule.class.uniqueId1 +0 -0
  98. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  99. package/android/build.gradle +107 -107
  100. package/android/src/main/AndroidManifest.xml +6 -6
  101. package/android/src/main/java/com/aguacerowx/reactnative/AguaceroPackage.java +33 -33
  102. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +732 -688
  103. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +232 -304
  104. package/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +125 -125
  105. package/android/src/main/java/com/aguacerowx/reactnative/InspectorModule.java +145 -71
  106. package/android/src/main/java/com/aguacerowx/reactnative/ShaderUtils.java +106 -106
  107. package/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +270 -151
  108. package/android/src/main/res/raw/debug_fragment_shader.glsl +12 -12
  109. package/android/src/main/res/raw/debug_vertex_shader.glsl +12 -12
  110. package/android/src/main/res/raw/fragment_shader.glsl +180 -180
  111. package/android/src/main/res/raw/vertex_shader.glsl +19 -19
  112. package/index.js +2 -2
  113. package/ios/AguaceroPackage.m +18 -18
  114. package/ios/FragmentUniforms.swift +15 -15
  115. package/ios/GridRenderLayer.swift +1158 -1114
  116. package/ios/GridRenderLayerBridge.swift +44 -44
  117. package/ios/GridRenderLayerManager.mm +171 -171
  118. package/ios/GridRenderLayerView.h +30 -30
  119. package/ios/GridRenderLayerView.m +200 -200
  120. package/ios/InspectorDataCache.swift +63 -63
  121. package/ios/InspectorModule.m +9 -9
  122. package/ios/InspectorModule.swift +112 -112
  123. package/ios/Shaders.metal +319 -319
  124. package/ios/WeatherFrameProcessorModule.m +15 -15
  125. package/ios/WeatherFrameProcessorModule.swift +152 -152
  126. package/lib/commonjs/README.md +126 -126
  127. package/lib/commonjs/aguacerowx-react-native.podspec +38 -38
  128. package/lib/commonjs/android/build/generated/source/buildConfig/debug/com/aguacerowx/reactnative/BuildConfig.java +10 -0
  129. package/lib/commonjs/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.java +43 -0
  130. package/lib/commonjs/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.java +22 -0
  131. package/lib/commonjs/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec-generated.cpp +22 -0
  132. package/lib/commonjs/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec.h +24 -0
  133. package/lib/commonjs/android/build/generated/source/codegen/jni/CMakeLists.txt +28 -0
  134. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI-generated.cpp +17 -0
  135. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI.h +19 -0
  136. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.cpp +22 -0
  137. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.h +24 -0
  138. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.cpp +16 -0
  139. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.h +23 -0
  140. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.cpp +62 -0
  141. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.h +40 -0
  142. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.cpp +17 -0
  143. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.h +32 -0
  144. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.cpp +16 -0
  145. package/lib/commonjs/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.h +20 -0
  146. package/lib/commonjs/android/build/generated/source/codegen/schema.json +1 -0
  147. package/lib/commonjs/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +8 -0
  148. package/lib/commonjs/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  149. package/lib/commonjs/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  150. package/lib/commonjs/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  151. package/lib/commonjs/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  152. package/lib/commonjs/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  153. package/lib/commonjs/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +4 -0
  154. package/lib/commonjs/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_fragment_shader.glsl.flat +0 -0
  155. package/lib/commonjs/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_vertex_shader.glsl.flat +0 -0
  156. package/lib/commonjs/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_fragment_shader.glsl.flat +0 -0
  157. package/lib/commonjs/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_vertex_shader.glsl.flat +0 -0
  158. package/lib/commonjs/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +5 -0
  159. package/lib/commonjs/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  160. package/lib/commonjs/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  161. package/lib/commonjs/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  162. package/lib/commonjs/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  163. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  164. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  165. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  166. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  167. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  168. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  169. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  170. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  171. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  172. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  173. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  174. package/lib/commonjs/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  175. package/lib/commonjs/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +6 -0
  176. package/lib/commonjs/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +8 -0
  177. package/lib/commonjs/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +8 -0
  178. package/lib/commonjs/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  179. package/lib/commonjs/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  180. package/lib/commonjs/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_fragment_shader.glsl +13 -0
  181. package/lib/commonjs/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_vertex_shader.glsl +13 -0
  182. package/lib/commonjs/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/fragment_shader.glsl +181 -0
  183. package/lib/commonjs/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/vertex_shader.glsl +20 -0
  184. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  185. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  186. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  187. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  188. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  189. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  190. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  191. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  192. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  193. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  194. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  195. package/lib/commonjs/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  196. package/lib/commonjs/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  197. package/lib/commonjs/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +5 -0
  198. package/lib/commonjs/android/build/outputs/logs/manifest-merger-debug-report.txt +17 -0
  199. package/lib/commonjs/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/AguaceroPackage.class.uniqueId2 +0 -0
  200. package/lib/commonjs/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule$1.class.uniqueId0 +0 -0
  201. package/lib/commonjs/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule.class.uniqueId1 +0 -0
  202. package/lib/commonjs/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  203. package/lib/commonjs/android/build.gradle +107 -107
  204. package/lib/commonjs/android/src/main/AndroidManifest.xml +6 -6
  205. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/AguaceroPackage.java +33 -33
  206. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +732 -688
  207. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +232 -304
  208. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +125 -125
  209. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/InspectorModule.java +145 -71
  210. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/ShaderUtils.java +106 -106
  211. package/lib/commonjs/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +270 -151
  212. package/lib/commonjs/android/src/main/res/raw/debug_fragment_shader.glsl +12 -12
  213. package/lib/commonjs/android/src/main/res/raw/debug_vertex_shader.glsl +12 -12
  214. package/lib/commonjs/android/src/main/res/raw/fragment_shader.glsl +180 -180
  215. package/lib/commonjs/android/src/main/res/raw/vertex_shader.glsl +19 -19
  216. package/lib/commonjs/babel.config.js.map +1 -1
  217. package/lib/commonjs/index.js.map +1 -1
  218. package/lib/commonjs/ios/AguaceroPackage.m +18 -18
  219. package/lib/commonjs/ios/FragmentUniforms.swift +15 -15
  220. package/lib/commonjs/ios/GridRenderLayer.swift +1158 -1114
  221. package/lib/commonjs/ios/GridRenderLayerBridge.swift +44 -44
  222. package/lib/commonjs/ios/GridRenderLayerManager.mm +171 -171
  223. package/lib/commonjs/ios/GridRenderLayerView.h +30 -30
  224. package/lib/commonjs/ios/GridRenderLayerView.m +200 -200
  225. package/lib/commonjs/ios/InspectorDataCache.swift +63 -63
  226. package/lib/commonjs/ios/InspectorModule.m +9 -9
  227. package/lib/commonjs/ios/InspectorModule.swift +112 -112
  228. package/lib/commonjs/ios/Shaders.metal +319 -319
  229. package/lib/commonjs/ios/WeatherFrameProcessorModule.m +15 -15
  230. package/lib/commonjs/ios/WeatherFrameProcessorModule.swift +152 -152
  231. package/lib/commonjs/package.json +72 -72
  232. package/lib/commonjs/react-native-builder-bob.config.js.map +1 -1
  233. package/lib/commonjs/scripts/compile-shaders.js.map +1 -1
  234. package/lib/commonjs/scripts/compile-shaders.sh +38 -38
  235. package/lib/commonjs/src/AguaceroContext.js.map +1 -1
  236. package/lib/commonjs/src/GridRenderLayer.js.map +1 -1
  237. package/lib/commonjs/src/GridRenderLayerNativeComponent.js.map +1 -1
  238. package/lib/commonjs/src/MapManager.js.map +1 -1
  239. package/lib/commonjs/src/MapRegistry.js.map +1 -1
  240. package/lib/commonjs/src/StyleApplicator.js +6 -6
  241. package/lib/commonjs/src/StyleApplicator.js.map +1 -1
  242. package/lib/commonjs/src/WeatherLayerManager.js +6 -42
  243. package/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
  244. package/lib/commonjs/tsconfig.json +23 -23
  245. package/lib/module/README.md +126 -126
  246. package/lib/module/aguacerowx-react-native.podspec +38 -38
  247. package/lib/module/android/build/generated/source/buildConfig/debug/com/aguacerowx/reactnative/BuildConfig.java +10 -0
  248. package/lib/module/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.java +43 -0
  249. package/lib/module/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.java +22 -0
  250. package/lib/module/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec-generated.cpp +22 -0
  251. package/lib/module/android/build/generated/source/codegen/jni/AguaceroWxReactNativeSpec.h +24 -0
  252. package/lib/module/android/build/generated/source/codegen/jni/CMakeLists.txt +28 -0
  253. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI-generated.cpp +17 -0
  254. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/AguaceroWxReactNativeSpecJSI.h +19 -0
  255. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.cpp +22 -0
  256. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ComponentDescriptors.h +24 -0
  257. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.cpp +16 -0
  258. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/EventEmitters.h +23 -0
  259. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.cpp +62 -0
  260. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/Props.h +40 -0
  261. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.cpp +17 -0
  262. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/ShadowNodes.h +32 -0
  263. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.cpp +16 -0
  264. package/lib/module/android/build/generated/source/codegen/jni/react/renderer/components/AguaceroWxReactNativeSpec/States.h +20 -0
  265. package/lib/module/android/build/generated/source/codegen/schema.json +1 -0
  266. package/lib/module/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +8 -0
  267. package/lib/module/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  268. package/lib/module/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  269. package/lib/module/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  270. package/lib/module/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  271. package/lib/module/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  272. package/lib/module/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +4 -0
  273. package/lib/module/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_fragment_shader.glsl.flat +0 -0
  274. package/lib/module/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_debug_vertex_shader.glsl.flat +0 -0
  275. package/lib/module/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_fragment_shader.glsl.flat +0 -0
  276. package/lib/module/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_vertex_shader.glsl.flat +0 -0
  277. package/lib/module/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +5 -0
  278. package/lib/module/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  279. package/lib/module/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  280. package/lib/module/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  281. package/lib/module/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  282. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  283. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  284. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  285. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  286. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  287. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  288. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  289. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  290. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  291. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  292. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  293. package/lib/module/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  294. package/lib/module/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +6 -0
  295. package/lib/module/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +8 -0
  296. package/lib/module/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +8 -0
  297. package/lib/module/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  298. package/lib/module/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  299. package/lib/module/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_fragment_shader.glsl +13 -0
  300. package/lib/module/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/debug_vertex_shader.glsl +13 -0
  301. package/lib/module/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/fragment_shader.glsl +181 -0
  302. package/lib/module/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/vertex_shader.glsl +20 -0
  303. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/AguaceroPackage.class +0 -0
  304. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/BuildConfig.class +0 -0
  305. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer$VertexInfo.class +0 -0
  306. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayer.class +0 -0
  307. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderLayerView.class +0 -0
  308. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/GridRenderManager.class +0 -0
  309. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/InspectorModule.class +0 -0
  310. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/ShaderUtils.class +0 -0
  311. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule$1.class +0 -0
  312. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/aguacerowx/reactnative/WeatherFrameProcessorModule.class +0 -0
  313. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerDelegate.class +0 -0
  314. package/lib/module/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/GridRenderLayerManagerInterface.class +0 -0
  315. package/lib/module/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  316. package/lib/module/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +5 -0
  317. package/lib/module/android/build/outputs/logs/manifest-merger-debug-report.txt +17 -0
  318. package/lib/module/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/AguaceroPackage.class.uniqueId2 +0 -0
  319. package/lib/module/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule$1.class.uniqueId0 +0 -0
  320. package/lib/module/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/WeatherFrameProcessorModule.class.uniqueId1 +0 -0
  321. package/lib/module/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  322. package/lib/module/android/build.gradle +107 -107
  323. package/lib/module/android/src/main/AndroidManifest.xml +6 -6
  324. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/AguaceroPackage.java +33 -33
  325. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayer.java +732 -688
  326. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderLayerView.java +232 -304
  327. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/GridRenderManager.java +125 -125
  328. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/InspectorModule.java +145 -71
  329. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/ShaderUtils.java +106 -106
  330. package/lib/module/android/src/main/java/com/aguacerowx/reactnative/WeatherFrameProcessorModule.java +270 -151
  331. package/lib/module/android/src/main/res/raw/debug_fragment_shader.glsl +12 -12
  332. package/lib/module/android/src/main/res/raw/debug_vertex_shader.glsl +12 -12
  333. package/lib/module/android/src/main/res/raw/fragment_shader.glsl +180 -180
  334. package/lib/module/android/src/main/res/raw/vertex_shader.glsl +19 -19
  335. package/lib/module/babel.config.js.map +1 -1
  336. package/lib/module/index.js.map +1 -1
  337. package/lib/module/ios/AguaceroPackage.m +18 -18
  338. package/lib/module/ios/FragmentUniforms.swift +15 -15
  339. package/lib/module/ios/GridRenderLayer.swift +1158 -1114
  340. package/lib/module/ios/GridRenderLayerBridge.swift +44 -44
  341. package/lib/module/ios/GridRenderLayerManager.mm +171 -171
  342. package/lib/module/ios/GridRenderLayerView.h +30 -30
  343. package/lib/module/ios/GridRenderLayerView.m +200 -200
  344. package/lib/module/ios/InspectorDataCache.swift +63 -63
  345. package/lib/module/ios/InspectorModule.m +9 -9
  346. package/lib/module/ios/InspectorModule.swift +112 -112
  347. package/lib/module/ios/Shaders.metal +319 -319
  348. package/lib/module/ios/WeatherFrameProcessorModule.m +15 -15
  349. package/lib/module/ios/WeatherFrameProcessorModule.swift +152 -152
  350. package/lib/module/lib/commonjs/README.md +126 -126
  351. package/lib/module/lib/commonjs/aguacerowx-react-native.podspec +38 -38
  352. package/lib/module/lib/commonjs/android/build.gradle +108 -0
  353. package/lib/module/lib/commonjs/android/src/main/AndroidManifest.xml +7 -0
  354. package/lib/module/lib/commonjs/babel.config.js.map +1 -1
  355. package/lib/module/lib/commonjs/index.js.map +1 -1
  356. package/lib/module/lib/commonjs/ios/AguaceroPackage.m +19 -0
  357. package/lib/module/lib/commonjs/ios/FragmentUniforms.swift +16 -0
  358. package/lib/module/lib/commonjs/ios/GridRenderLayer.swift +1159 -0
  359. package/lib/module/lib/commonjs/ios/GridRenderLayerBridge.swift +44 -44
  360. package/lib/module/lib/commonjs/ios/GridRenderLayerManager.mm +171 -171
  361. package/lib/module/lib/commonjs/ios/GridRenderLayerView.h +30 -30
  362. package/lib/module/lib/commonjs/ios/GridRenderLayerView.m +200 -200
  363. package/lib/module/lib/commonjs/ios/InspectorDataCache.swift +63 -63
  364. package/lib/module/lib/commonjs/ios/InspectorModule.m +9 -9
  365. package/lib/module/lib/commonjs/ios/InspectorModule.swift +112 -112
  366. package/lib/module/lib/commonjs/ios/Shaders.metal +319 -319
  367. package/lib/module/lib/commonjs/ios/WeatherFrameProcessorModule.m +15 -15
  368. package/lib/module/lib/commonjs/ios/WeatherFrameProcessorModule.swift +152 -152
  369. package/lib/module/lib/commonjs/ios/compiled-shaders/Shaders-device.metallib +0 -0
  370. package/lib/module/lib/commonjs/ios/generated/AguaceroWxReactNativeSpec-generated.mm +0 -0
  371. package/lib/module/lib/commonjs/ios/generated/AguaceroWxReactNativeSpec.h +0 -0
  372. package/lib/module/lib/commonjs/package.json +72 -72
  373. package/lib/module/lib/commonjs/react-native-builder-bob.config.js.map +1 -1
  374. package/lib/module/lib/commonjs/scripts/compile-shaders.js.map +1 -1
  375. package/lib/module/lib/commonjs/scripts/compile-shaders.sh +38 -38
  376. package/lib/module/lib/commonjs/src/AguaceroContext.js.map +1 -1
  377. package/lib/module/lib/commonjs/src/GridRenderLayer.js.map +1 -1
  378. package/lib/module/lib/commonjs/src/GridRenderLayerNativeComponent.js.map +1 -1
  379. package/lib/module/lib/commonjs/src/MapManager.js.map +1 -1
  380. package/lib/module/lib/commonjs/src/MapRegistry.js.map +1 -1
  381. package/lib/module/lib/commonjs/src/StyleApplicator.js +6 -6
  382. package/lib/module/lib/commonjs/src/StyleApplicator.js.map +1 -1
  383. package/lib/module/lib/commonjs/src/WeatherLayerManager.js +6 -42
  384. package/lib/module/lib/commonjs/src/WeatherLayerManager.js.map +1 -1
  385. package/lib/module/lib/commonjs/tsconfig.json +23 -23
  386. package/lib/module/package.json +72 -72
  387. package/lib/module/react-native-builder-bob.config.js.map +1 -1
  388. package/lib/module/scripts/compile-shaders.js.map +1 -1
  389. package/lib/module/scripts/compile-shaders.sh +38 -38
  390. package/lib/module/src/AguaceroContext.js.map +1 -1
  391. package/lib/module/src/GridRenderLayer.js.map +1 -1
  392. package/lib/module/src/GridRenderLayerNativeComponent.js.map +1 -1
  393. package/lib/module/src/MapManager.js.map +1 -1
  394. package/lib/module/src/MapRegistry.js.map +1 -1
  395. package/lib/module/src/StyleApplicator.js +6 -6
  396. package/lib/module/src/StyleApplicator.js.map +1 -1
  397. package/lib/module/src/WeatherLayerManager.js +6 -42
  398. package/lib/module/src/WeatherLayerManager.js.map +1 -1
  399. package/lib/module/tsconfig.json +23 -23
  400. package/lib/typescript/src/WeatherLayerManager.d.ts.map +1 -1
  401. package/package.json +72 -72
  402. package/src/AguaceroContext.js +3 -3
  403. package/src/GridRenderLayer.js +215 -215
  404. package/src/GridRenderLayerNativeComponent.ts +15 -15
  405. package/src/MapManager.js +218 -218
  406. package/src/MapRegistry.js +34 -34
  407. package/src/StyleApplicator.js +240 -240
  408. package/src/WeatherLayerManager.js +1163 -1207
@@ -1,1208 +1,1164 @@
1
- // packages/react-native/src/WeatherLayerManager.js
2
-
3
- import { AguaceroCore, DICTIONARIES, getUnitConversionFunction } from '@aguacerowx/javascript-sdk';
4
- import { fromByteArray } from 'base64-js';
5
- import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
6
- import { NativeModules, Platform } from 'react-native';
7
- import { AguaceroContext } from './AguaceroContext';
8
- import { GridRenderLayer } from './GridRenderLayer';
9
- import { mapRegistry } from './MapRegistry';
10
-
11
- // --- PERFORMANCE HELPER ---
12
- const perfLog = (msg) => {
13
- // Enable this if you want JS logs alongside native logs
14
- console.log(`⚡️ [PERF] [JS] ${msg} | ${performance.now().toFixed(2)}ms`);
15
- };
16
- // --------------------------
17
-
18
- function findLatestModelRun(modelsData, modelName) {
19
- const model = modelsData?.[modelName];
20
- if (!model) return null;
21
- const availableDates = Object.keys(model).sort((a, b) => b.localeCompare(a));
22
- for (const date of availableDates) {
23
- const runs = model[date];
24
- if (!runs) continue;
25
- const availableRuns = Object.keys(runs).sort((a, b) => b.localeCompare(a));
26
- if (availableRuns.length > 0) return { date: date, run: availableRuns[0] };
27
- }
28
- return null;
29
- }
30
- const { WeatherFrameProcessorModule, InspectorModule } = NativeModules;
31
-
32
- /**
33
- * A helper function to generate the raw RGBA byte buffer for the colormap texture.
34
- */
35
- const _generateColormapBytes = (colormap) => {
36
- const width = 256;
37
- const data = new Uint8Array(width * 4);
38
- const stops = colormap.reduce((acc, _, i) => (i % 2 === 0 ? [...acc, { value: colormap[i], color: colormap[i + 1] }] : acc), []);
39
-
40
- if (stops.length === 0) return data;
41
-
42
- const minVal = stops[0].value;
43
- const maxVal = stops[stops.length - 1].value;
44
-
45
- const hexToRgb = (hex) => {
46
- const r = parseInt(hex.slice(1, 3), 16);
47
- const g = parseInt(hex.slice(3, 5), 16);
48
- const b = parseInt(hex.slice(5, 7), 16);
49
- return [r, g, b];
50
- };
51
-
52
- for (let i = 0; i < width; i++) {
53
- const val = minVal + (i / (width - 1)) * (maxVal - minVal);
54
- let lower = stops[0];
55
- let upper = stops[stops.length - 1];
56
- for (let j = 0; j < stops.length - 1; j++) {
57
- if (val >= stops[j].value && val <= stops[j + 1].value) {
58
- lower = stops[j];
59
- upper = stops[j + 1];
60
- break;
61
- }
62
- }
63
- const t = (val - lower.value) / (upper.value - lower.value || 1);
64
- const lowerRgb = hexToRgb(lower.color);
65
- const upperRgb = hexToRgb(upper.color);
66
- const rgb = lowerRgb.map((c, idx) => c * (1 - t) + upperRgb[idx] * t);
67
-
68
- const offset = i * 4;
69
- data[offset + 0] = Math.round(rgb[0]);
70
- data[offset + 1] = Math.round(rgb[1]);
71
- data[offset + 2] = Math.round(rgb[2]);
72
- data[offset + 3] = 255;
73
- }
74
- return data;
75
- };
76
-
77
- AguaceroCore.prototype.setMapCenter = function (center) {
78
- this.emit('map:move', center);
79
- };
80
-
81
- export const WeatherLayerManager = forwardRef((props, ref) => {
82
- const {
83
- inspectorEnabled,
84
- onInspect,
85
- apiKey,
86
- customColormaps,
87
- initialMode,
88
- initialVariable,
89
- autoRefresh,
90
- autoRefreshInterval,
91
- initialModel,
92
- ...restProps
93
- } = props;
94
- const context = useContext(AguaceroContext);
95
-
96
- // Create the core here instead of getting it from context
97
- const core = useMemo(() => new AguaceroCore({
98
- apiKey: apiKey,
99
- customColormaps: customColormaps,
100
- // ADD: Pass layerOptions to the core's constructor
101
- layerOptions: {
102
- mode: initialMode,
103
- variable: initialVariable,
104
- model: initialModel
105
- }
106
- }), [apiKey]);
107
-
108
- const gridLayerRef = useRef(null);
109
- const currentGridDataRef = useRef(null);
110
- const autoRefreshIntervalId = useRef(null);
111
-
112
- // Cache for preloaded grid data - stores the processed data ready for GPU upload
113
- const preloadedDataCache = useRef(new Map());
114
-
115
- // Store geometry and colormap that don't change with forecast hour
116
- const cachedGeometry = useRef(null);
117
- const cachedColormap = useRef(null);
118
- const cachedDataRange = useRef([0, 1]);
119
-
120
- // Track if we've done the initial load
121
- const hasInitialLoad = useRef(false);
122
- const hasPreloadedRef = useRef(false);
123
-
124
- // Track the last state we processed to avoid redundant updates
125
- const lastProcessedState = useRef(null);
126
- const previousStateRef = useRef(null);
127
-
128
- const [renderProps, setRenderProps] = useState({
129
- opacity: 1,
130
- dataRange: [0, 1]
131
- });
132
-
133
- useImperativeHandle(ref, () => {
134
- const setAutoRefresh = (enabled, intervalSeconds) => {
135
- if (autoRefreshIntervalId.current) {
136
- clearInterval(autoRefreshIntervalId.current);
137
- autoRefreshIntervalId.current = null;
138
- }
139
- if (enabled) {
140
- const effectiveInterval = (intervalSeconds || autoRefreshInterval || 30) * 1000;
141
- // Run once immediately, then start the interval
142
- _checkForUpdates();
143
- autoRefreshIntervalId.current = setInterval(_checkForUpdates, effectiveInterval);
144
- }
145
- };
146
- return {
147
- play: () => {
148
- core.play();
149
- },
150
- pause: () => {
151
- core.pause();
152
- },
153
- togglePlay: () => {
154
- core.togglePlay();
155
- },
156
- step: (direction) => {
157
- core.step(direction);
158
- },
159
- setPlaybackSpeed: (speed) => {
160
- if (speed > 0) {
161
- core.playbackSpeed = speed;
162
- if (core.isPlaying) {
163
- core.pause();
164
- core.play();
165
- }
166
- }
167
- },
168
- setOpacity: (opacity) => core.setOpacity(opacity),
169
- setUnits: (units) => core.setUnits(units),
170
- switchMode: (options) => core.switchMode(options),
171
- getAvailableVariables: (model) => core.getAvailableVariables(model),
172
- getVariableDisplayName: (code) => core.getVariableDisplayName(code),
173
- setRun: (runString) => core.setState({ run: runString.split(':')[1] }),
174
- setState: (newState) => core.setState(newState),
175
- setMRMSTimestamp: (timestamp) => core.setMRMSTimestamp(timestamp),
176
- setSmoothing: (enabled) => {
177
- if (gridLayerRef.current) {
178
- gridLayerRef.current.setSmoothing(enabled);
179
- }
180
- },
181
- setAutoRefresh,
182
- refreshData: () => {
183
- _checkForUpdates();
184
- },
185
- };
186
- }, [core, autoRefreshInterval, _checkForUpdates]);
187
-
188
- const preloadAllFramesToDisk = async (state) => {
189
- const start = performance.now();
190
- perfLog("preloadAllFramesToDisk called");
191
-
192
- if (hasPreloadedRef.current) {
193
- return;
194
- }
195
-
196
- const { isMRMS, model, date, run, variable, units, availableHours, availableTimestamps, forecastHour, mrmsTimestamp } = state;
197
-
198
- // CRITICAL: Don't start preloading if we don't have a valid current frame
199
- if (isMRMS && (mrmsTimestamp == null || !availableTimestamps || availableTimestamps.length === 0)) {
200
- hasPreloadedRef.current = false;
201
- return;
202
- }
203
-
204
- if (!isMRMS && (forecastHour == null || !availableHours || availableHours.length === 0)) {
205
- hasPreloadedRef.current = false;
206
- return;
207
- }
208
-
209
- // Only mark as "has preloaded" after validation passes
210
- hasPreloadedRef.current = true;
211
-
212
- // Fix the current forecast hour if it's invalid for this variable/model combo
213
- let effectiveForecastHour = forecastHour;
214
- if (!isMRMS && variable === 'ptypeRefl' && model === 'hrrr' && forecastHour === 0) {
215
- const validHours = availableHours.filter(hour => hour !== 0);
216
- effectiveForecastHour = validHours.length > 0 ? validHours[0] : 0;
217
- }
218
-
219
- if (!cachedGeometry.current || !cachedColormap.current) {
220
- const gridModel = isMRMS ? 'mrms' : model;
221
- const { corners, gridDef } = core._getGridCornersAndDef(gridModel);
222
- gridLayerRef.current.updateGeometry(corners, gridDef);
223
- cachedGeometry.current = { model: gridModel, variable };
224
-
225
- const { colormap, baseUnit } = core._getColormapForVariable(variable);
226
- const toUnit = core._getTargetUnit(baseUnit, units);
227
- const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
228
- let dataRange;
229
- if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
230
- dataRange = isMRMS ? [5, 380] : [5, 380];
231
- } else {
232
- dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
233
- }
234
- const colormapBytes = _generateColormapBytes(finalColormap);
235
- const colormapAsBase64 = fromByteArray(colormapBytes);
236
-
237
- gridLayerRef.current.updateColormapTexture(colormapAsBase64);
238
- cachedColormap.current = { key: `${variable}-${units}` };
239
- cachedDataRange.current = dataRange;
240
-
241
- setRenderProps({ opacity: state.opacity, dataRange: dataRange });
242
- hasInitialLoad.current = true;
243
- }
244
-
245
- // Apply the same filtering logic as in AguaceroCore._emitStateChange
246
- let filteredHours = availableHours;
247
- if (!isMRMS && variable === 'ptypeRefl' && model === 'hrrr' && availableHours && availableHours.length > 0) {
248
- filteredHours = availableHours.filter(hour => hour !== 0);
249
- }
250
-
251
- const allFrames = isMRMS ? availableTimestamps : filteredHours;
252
- if (!allFrames || allFrames.length === 0) {
253
- return;
254
- }
255
-
256
- const currentFrame = isMRMS ? mrmsTimestamp : effectiveForecastHour;
257
-
258
- // Double-check currentFrame is valid
259
- if (currentFrame == null) {
260
- hasPreloadedRef.current = false;
261
- }
262
-
263
- // Reverse the frame order to load from last to first
264
- const reversedFrames = [...allFrames].reverse();
265
- const framesToPreload = reversedFrames.filter(frame => frame !== currentFrame);
266
-
267
- const { corners, gridDef } = core._getGridCornersAndDef(isMRMS ? 'mrms' : model);
268
- const { nx, ny } = gridDef.grid_params;
269
-
270
- // Load the current frame FIRST and WAIT for it before continuing
271
- const currentCacheKey = isMRMS ? `mrms-${currentFrame}-${variable}` : `${model}-${date}-${run}-${currentFrame}-${variable}`;
272
-
273
- if (!preloadedDataCache.current.has(currentCacheKey)) {
274
- let resourcePath;
275
- if (isMRMS) {
276
- const frameDate = new Date(currentFrame * 1000);
277
- const y = frameDate.getUTCFullYear();
278
- const m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0');
279
- const d = frameDate.getUTCDate().toString().padStart(2, '0');
280
- resourcePath = `/grids/mrms/${y}${m}${d}/${currentFrame}/0/${variable}/0`;
281
- } else {
282
- resourcePath = `/grids/${model}/${date}/${run}/${currentFrame}/${variable}/0`;
283
- }
284
-
285
- const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
286
- const options = { url, apiKey: core.apiKey, bundleId: core.bundleId };
287
-
288
- try {
289
- const netStart = performance.now();
290
- const result = await WeatherFrameProcessorModule.processFrame(options);
291
- perfLog(`First frame fetched in ${(performance.now() - netStart).toFixed(2)}ms`);
292
-
293
- if (!result || !result.filePath) {
294
- return;
295
- }
296
-
297
- const { baseUnit } = core._getColormapForVariable(variable);
298
-
299
- const toUnit = core._getTargetUnit(baseUnit, units);
300
-
301
- const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
302
- const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
303
-
304
- let dataScale = result.scale;
305
- let dataOffset = result.offset;
306
-
307
- let convertedScale = dataScale;
308
- let convertedOffset = dataOffset;
309
-
310
- if (serverDataUnit !== baseUnit) {
311
- const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
312
- if (conversionFunc) {
313
- if (result.scaleType === 'sqrt') {
314
- const physicalAtOffset = dataOffset * dataOffset;
315
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
316
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
317
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
318
- convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
319
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
320
- convertedScale = newOffsetPlusScale - convertedOffset;
321
- } else {
322
- convertedOffset = conversionFunc(dataOffset);
323
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
324
- convertedScale = convertedOffsetPlusScale - convertedOffset;
325
- }
326
- dataScale = convertedScale;
327
- dataOffset = convertedOffset;
328
- }
329
- }
330
-
331
- if (baseUnit !== toUnit) {
332
- const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
333
- if (conversionFunc) {
334
- if (result.scaleType === 'sqrt') {
335
- const physicalAtOffset = dataOffset * dataOffset;
336
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
337
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
338
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
339
- convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
340
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
341
- convertedScale = newOffsetPlusScale - convertedOffset;
342
- } else {
343
- convertedOffset = conversionFunc(dataOffset);
344
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
345
- convertedScale = convertedOffsetPlusScale - convertedOffset;
346
- }
347
- }
348
- }
349
-
350
- const frameData = {
351
- filePath: result.filePath,
352
- nx, ny,
353
- scale: convertedScale,
354
- offset: convertedOffset,
355
- missing: result.missing,
356
- corners,
357
- gridDef,
358
- scaleType: result.scaleType,
359
- originalScale: result.scale,
360
- originalOffset: result.offset
361
- };
362
-
363
- preloadedDataCache.current.set(currentCacheKey, frameData);
364
-
365
- // Update the GPU with the current frame
366
- gridLayerRef.current.updateDataTextureFromFile(
367
- frameData.filePath,
368
- frameData.nx, frameData.ny,
369
- frameData.scale, frameData.offset, frameData.missing,
370
- frameData.scaleType
371
- );
372
-
373
- if (Platform.OS === 'ios' && gridLayerRef.current.primeGpuCache) {
374
- gridLayerRef.current.primeGpuCache({
375
- [currentCacheKey]: {
376
- filePath: frameData.filePath,
377
- nx: frameData.nx,
378
- ny: frameData.ny,
379
- scale: frameData.scale,
380
- offset: frameData.offset,
381
- missing: frameData.missing,
382
- scaleType: frameData.scaleType || 'linear',
383
- originalScale: frameData.originalScale,
384
- originalOffset: frameData.originalOffset
385
- }
386
- });
387
- }
388
-
389
- perfLog(`First frame updated to GPU`);
390
-
391
- currentGridDataRef.current = {
392
- nx: frameData.nx,
393
- ny: frameData.ny,
394
- scale: frameData.scale,
395
- offset: frameData.offset,
396
- missing: frameData.missing,
397
- gridDef: frameData.gridDef,
398
- variable: variable,
399
- units: units,
400
- scaleType: frameData.scaleType
401
- };
402
- } catch (error) {
403
- console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${currentFrame}:`, error);
404
- hasPreloadedRef.current = false;
405
- }
406
- }
407
-
408
- // NOW preload the rest of the frames asynchronously
409
- perfLog(`Triggering fetch for ${framesToPreload.length} background frames`);
410
-
411
- framesToPreload.forEach((frame) => {
412
- const cacheKey = isMRMS ? `mrms-${frame}-${variable}` : `${model}-${date}-${run}-${frame}-${variable}`;
413
- if (preloadedDataCache.current.has(cacheKey)) {
414
- return;
415
- }
416
-
417
- let resourcePath;
418
- if (isMRMS) {
419
- const frameDate = new Date(frame * 1000);
420
- const y = frameDate.getUTCFullYear();
421
- const m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0');
422
- const d = frameDate.getUTCDate().toString().padStart(2, '0');
423
- resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${variable}/0`;
424
- } else {
425
- resourcePath = `/grids/${model}/${date}/${run}/${frame}/${variable}/0`;
426
- }
427
-
428
- const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
429
- const options = { url, apiKey: core.apiKey, bundleId: core.bundleId };
430
-
431
- WeatherFrameProcessorModule.processFrame(options)
432
- .then(result => {
433
- if (!result || !result.filePath) {
434
- console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${frame}: No filePath`);
435
- return;
436
- }
437
-
438
- // ADD: Same two-step conversion as the current frame
439
- const { baseUnit } = core._getColormapForVariable(variable);
440
- const toUnit = core._getTargetUnit(baseUnit, units);
441
- const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
442
- const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
443
-
444
- let dataScale = result.scale;
445
- let dataOffset = result.offset;
446
-
447
- let convertedScale = dataScale;
448
- let convertedOffset = dataOffset;
449
-
450
- // Step 1: Convert from server unit to colormap base unit
451
- if (serverDataUnit !== baseUnit) {
452
- const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
453
- if (conversionFunc) {
454
- if (result.scaleType === 'sqrt') {
455
- const physicalAtOffset = dataOffset * dataOffset;
456
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
457
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
458
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
459
- convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
460
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
461
- convertedScale = newOffsetPlusScale - convertedOffset;
462
- } else {
463
- convertedOffset = conversionFunc(dataOffset);
464
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
465
- convertedScale = convertedOffsetPlusScale - convertedOffset;
466
- }
467
- dataScale = convertedScale;
468
- dataOffset = convertedOffset;
469
- }
470
- }
471
-
472
- // Step 2: Convert from colormap base unit to target display unit
473
- if (baseUnit !== toUnit) {
474
- const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
475
- if (conversionFunc) {
476
- if (result.scaleType === 'sqrt') {
477
- const physicalAtOffset = dataOffset * dataOffset;
478
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
479
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
480
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
481
- convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
482
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
483
- convertedScale = newOffsetPlusScale - convertedOffset;
484
- } else {
485
- convertedOffset = conversionFunc(dataOffset);
486
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
487
- convertedScale = convertedOffsetPlusScale - convertedOffset;
488
- }
489
- }
490
- }
491
-
492
- const frameData = {
493
- filePath: result.filePath,
494
- nx, ny,
495
- scale: convertedScale,
496
- offset: convertedOffset,
497
- missing: result.missing,
498
- corners,
499
- gridDef,
500
- scaleType: result.scaleType,
501
- originalScale: result.scale,
502
- originalOffset: result.offset
503
- };
504
-
505
- preloadedDataCache.current.set(cacheKey, frameData);
506
-
507
- if (Platform.OS === 'ios' && gridLayerRef.current.primeGpuCache) {
508
- const frameInfoForGpu = {
509
- [cacheKey]: {
510
- filePath: frameData.filePath,
511
- nx: frameData.nx,
512
- ny: frameData.ny,
513
- scale: frameData.scale,
514
- offset: frameData.offset,
515
- missing: frameData.missing,
516
- scaleType: frameData.scaleType || 'linear',
517
- originalScale: frameData.originalScale,
518
- originalOffset: frameData.originalOffset
519
- }
520
- };
521
- gridLayerRef.current.primeGpuCache(frameInfoForGpu);
522
- }
523
- })
524
- .catch(error => {
525
- console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${frame}:`, error);
526
- });
527
- });
528
-
529
- perfLog(`Completed preload triggering in ${(performance.now() - start).toFixed(2)}ms`);
530
- };
531
-
532
- useEffect(() => {
533
- // This effect manages the auto-refresh based on props
534
- if (autoRefresh) {
535
- const interval = (autoRefreshInterval || 30) * 1000;
536
- _checkForUpdates(); // Run immediately on enable
537
- autoRefreshIntervalId.current = setInterval(_checkForUpdates, interval);
538
- }
539
-
540
- // Cleanup function: this runs when the component unmounts or props change
541
- return () => {
542
- if (autoRefreshIntervalId.current) {
543
- clearInterval(autoRefreshIntervalId.current);
544
- autoRefreshIntervalId.current = null;
545
- }
546
- };
547
- }, [autoRefresh, autoRefreshInterval, _checkForUpdates]);
548
-
549
- const updateGPUWithCachedData = (state) => {
550
- const start = performance.now();
551
- const { model, date, run, forecastHour, variable, units, isMRMS, mrmsTimestamp } = state;
552
-
553
- const cacheKey = isMRMS
554
- ? `mrms-${mrmsTimestamp}-${variable}`
555
- : `${model}-${date}-${run}-${forecastHour}-${variable}`;
556
-
557
- if (Platform.OS === 'ios' && gridLayerRef.current.setActiveFrame) {
558
- // Get the cached data BEFORE calling setActiveFrame
559
- const cachedData = preloadedDataCache.current.get(cacheKey);
560
-
561
- if (cachedData) {
562
- currentGridDataRef.current = {
563
- nx: cachedData.nx,
564
- ny: cachedData.ny,
565
- scale: cachedData.scale,
566
- offset: cachedData.offset,
567
- missing: cachedData.missing,
568
- gridDef: cachedData.gridDef,
569
- variable: variable,
570
- units: units,
571
- scaleType: cachedData.scaleType
572
- };
573
- }
574
-
575
- if (__DEV__) {
576
- console.log(`[WeatherLayerManager] setActiveFrame: ${cacheKey}`);
577
- }
578
- gridLayerRef.current.setActiveFrame(cacheKey);
579
- perfLog(`setActiveFrame sent in ${(performance.now() - start).toFixed(2)}ms`);
580
- return true;
581
- }
582
-
583
- const cachedData = preloadedDataCache.current.get(cacheKey);
584
-
585
- if (!cachedData) {
586
- return false;
587
- }
588
-
589
- if (!gridLayerRef.current) {
590
- console.warn(`⚠️ [updateGPUWithCachedData] GridLayer ref not available`); // CHANGED
591
- return false;
592
- }
593
-
594
- if (!cachedGeometry.current || cachedGeometry.current.model !== (isMRMS ? 'mrms' : model) || cachedGeometry.current.variable !== variable) {
595
- gridLayerRef.current.updateGeometry(cachedData.corners, cachedData.gridDef);
596
- cachedGeometry.current = { model: (isMRMS ? 'mrms' : model), variable };
597
- }
598
-
599
- const colormapKey = `${variable}-${units}`;
600
- if (!cachedColormap.current || cachedColormap.current.key !== colormapKey) {
601
- const { colormap, baseUnit } = core._getColormapForVariable(variable);
602
- const toUnit = core._getTargetUnit(baseUnit, units);
603
- const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
604
- let dataRange;
605
- if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
606
- if (isMRMS) {
607
- dataRange = [5, 380];
608
- } else {
609
- dataRange = [5, 380];
610
- }
611
- } else {
612
- dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
613
- }
614
- const colormapBytes = _generateColormapBytes(finalColormap);
615
- const colormapAsBase64 = fromByteArray(colormapBytes);
616
-
617
- gridLayerRef.current.updateColormapTexture(colormapAsBase64);
618
- cachedColormap.current = { key: colormapKey };
619
- cachedDataRange.current = dataRange;
620
-
621
- setRenderProps(prev => ({ ...prev, dataRange }));
622
- }
623
-
624
- if (cachedData.filePath) {
625
- gridLayerRef.current.updateDataTextureFromFile(
626
- cachedData.filePath,
627
- cachedData.nx, cachedData.ny,
628
- cachedData.scale, cachedData.offset, cachedData.missing,
629
- cachedData.scaleType
630
- );
631
-
632
- // ADD THIS: Update inspector cache for file-based data too
633
- currentGridDataRef.current = {
634
- nx: cachedData.nx,
635
- ny: cachedData.ny,
636
- scale: cachedData.scale,
637
- offset: cachedData.offset,
638
- missing: cachedData.missing,
639
- gridDef: cachedData.gridDef,
640
- variable: variable,
641
- units: units
642
- };
643
- } else if (cachedData.dataAsBase64) {
644
- gridLayerRef.current.updateDataTexture(
645
- cachedData.dataAsBase64,
646
- cachedData.nx, cachedData.ny,
647
- cachedData.scale, cachedData.offset, cachedData.missing,
648
- cachedData.scaleType
649
- );
650
-
651
- // Update the inspector cache when using dataAsBase64
652
- const binaryString = atob(cachedData.dataAsBase64);
653
- const uint8Array = new Uint8Array(binaryString.length);
654
-
655
- for (let i = 0; i < binaryString.length; i++) {
656
- uint8Array[i] = binaryString.charCodeAt(i);
657
- }
658
-
659
- currentGridDataRef.current = {
660
- data: uint8Array,
661
- nx: cachedData.nx,
662
- ny: cachedData.ny,
663
- scale: cachedData.scale,
664
- offset: cachedData.offset,
665
- missing: cachedData.missing,
666
- gridDef: cachedData.gridDef,
667
- variable: variable,
668
- units: units
669
- };
670
- } else {
671
- console.error(`❌ [updateGPUWithCachedData] Cached data has no filePath or dataAsBase64!`);
672
- console.error('❌ [updateGPUWithCachedData] Cached data keys:', Object.keys(cachedData));
673
- return false;
674
- }
675
-
676
- // Update inspector parameters for file-based data too
677
- if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
678
- gridLayerRef.current.updateDataParameters(cachedData.scale, cachedData.offset, cachedData.missing);
679
- }
680
- return true;
681
- };
682
-
683
- const handleStateChangeRef = useRef(null);
684
- const debounceTimeoutRef = useRef(null);
685
-
686
- useEffect(() => {
687
- if (core && props.customColormaps) {
688
- core.customColormaps = props.customColormaps;
689
- // Trigger a re-render if we already have data loaded
690
- if (hasInitialLoad.current) {
691
- core._emitStateChange();
692
- }
693
- }
694
- }, [core, props.customColormaps]);
695
-
696
- const getValueAtPoint = async (lng, lat) => {
697
- if (!core) {
698
- console.warn('🔍 [Inspector] Core not available');
699
- return null;
700
- }
701
-
702
- // ADD THIS: Check if we have valid data before attempting inspection
703
- if (!currentGridDataRef.current) {
704
- return null;
705
- }
706
-
707
- try {
708
- const gridIndices = core._getGridIndexFromLngLat(lng, lat);
709
- if (!gridIndices) return null;
710
-
711
- const { i, j } = gridIndices;
712
-
713
- const value = await InspectorModule.getValueAtGridIndex(i, j);
714
-
715
- if (value === null) {
716
- return null;
717
- }
718
-
719
- const { colormap, baseUnit } = core._getColormapForVariable(core.state.variable);
720
- const displayUnit = core._getTargetUnit(baseUnit, core.state.units);
721
- const finalColormap = core._convertColormapUnits(colormap, baseUnit, displayUnit);
722
- const minThreshold = finalColormap[0];
723
-
724
- if (value < minThreshold) {
725
- return null;
726
- }
727
-
728
- // Filter out values below the minimum threshold (matching shader behavior)
729
- if (value < minThreshold) {
730
- return null;
731
- }
732
-
733
- // Also check if value is NaN or effectively missing
734
- if (!isFinite(value)) {
735
- return null;
736
- }
737
-
738
- return {
739
- value: value,
740
- unit: displayUnit,
741
- variable: {
742
- code: core.state.variable,
743
- name: core.getVariableDisplayName(core.state.variable)
744
- },
745
- lngLat: { lng, lat }
746
- };
747
- } catch (error) {
748
- console.error('🔍 [Inspector] Error:', error);
749
- return null;
750
- }
751
- };
752
-
753
- const _checkForUpdates = useMemo(() => async () => {
754
- if (!core) return;
755
- const { isMRMS, model: currentModel, variable: currentVariable, date, run } = core.state;
756
-
757
- if (isMRMS) {
758
- // --- MRMS LOGIC (Keep existing logic) ---
759
- const oldTimestamps = new Set(core.mrmsStatus?.[currentVariable] || []);
760
- const mrmsStatus = await core.fetchMRMSStatus(true);
761
- const newTimestamps = mrmsStatus?.[currentVariable] || [];
762
- if (newTimestamps.length === 0) return;
763
-
764
- const newTimestampsToPreload = newTimestamps.filter(ts => !oldTimestamps.has(ts));
765
-
766
- if (newTimestampsToPreload.length > 0) {
767
- core.mrmsStatus = mrmsStatus;
768
- core._emitStateChange(); // Update UI slider without changing selection
769
-
770
- // ... (Keep your existing preloading logic here) ...
771
- const { corners, gridDef } = core._getGridCornersAndDef('mrms');
772
- const { nx, ny } = gridDef.grid_params;
773
-
774
- newTimestampsToPreload.forEach(frame => {
775
- const cacheKey = `mrms-${frame}-${currentVariable}`;
776
- if (preloadedDataCache.current.has(cacheKey)) return;
777
-
778
- const frameDate = new Date(frame * 1000);
779
- const y = frameDate.getUTCFullYear(), m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'), d = frameDate.getUTCDate().toString().padStart(2, '0');
780
- const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
781
- const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
782
-
783
- WeatherFrameProcessorModule.processFrame({ url, apiKey: core.apiKey, bundleId: core.bundleId })
784
- .then(result => {
785
- if (!result || !result.filePath) return;
786
- const frameData = { filePath: result.filePath, nx, ny, scale: result.scale, offset: result.offset, missing: result.missing, corners, gridDef, scaleType: result.scaleType, originalScale: result.scale, originalOffset: result.offset };
787
- preloadedDataCache.current.set(cacheKey, frameData);
788
- if (Platform.OS === 'ios' && gridLayerRef.current?.primeGpuCache) {
789
- gridLayerRef.current.primeGpuCache({ [cacheKey]: frameData });
790
- }
791
- }).catch(error => console.warn(`[Auto-Refresh] Failed to preload frame ${frame}:`, error));
792
- });
793
-
794
- const newTimestampsSet = new Set(newTimestamps);
795
- oldTimestamps.forEach(oldTs => {
796
- if (!newTimestampsSet.has(oldTs)) {
797
- const cacheKey = `mrms-${oldTs}-${currentVariable}`;
798
- preloadedDataCache.current.delete(cacheKey);
799
- }
800
- });
801
- }
802
- } else {
803
- const modelStatus = await core.fetchModelStatus(true);
804
- core._emitStateChange();
805
-
806
- const latestRun = findLatestModelRun(modelStatus, currentModel);
807
- if (!latestRun) return;
808
- }
809
- }, [core]);
810
-
811
- useEffect(() => {
812
- if (!core) {
813
- console.warn('⚠️ [useEffect] Core is not available yet');
814
- return;
815
- }
816
-
817
- const handleStateChange = (newState) => {
818
- if (!previousStateRef.current) {
819
- previousStateRef.current = core.state;
820
- }
821
-
822
- const variableChanged = !previousStateRef.current || newState.variable !== previousStateRef.current.variable;
823
-
824
- if (variableChanged && gridLayerRef.current?.setVariable) {
825
- gridLayerRef.current.setVariable(newState.variable);
826
- }
827
-
828
- const stateKey = `${newState.model}-${newState.variable}-${newState.date}-${newState.run}-${newState.forecastHour}-${newState.units}-${newState.mrmsTimestamp}`;
829
-
830
- const isOpacityOnlyChange =
831
- hasInitialLoad.current &&
832
- newState.opacity !== renderProps.opacity &&
833
- newState.variable === previousStateRef.current?.variable &&
834
- newState.forecastHour === previousStateRef.current?.forecastHour &&
835
- newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp &&
836
- newState.model === previousStateRef.current?.model &&
837
- newState.units === previousStateRef.current?.units;
838
-
839
- const isPlayStateOnlyChange =
840
- hasInitialLoad.current &&
841
- newState.isPlaying !== previousStateRef.current?.isPlaying &&
842
- newState.variable === previousStateRef.current?.variable &&
843
- newState.forecastHour === previousStateRef.current?.forecastHour &&
844
- newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp &&
845
- newState.model === previousStateRef.current?.model &&
846
- newState.units === previousStateRef.current?.units &&
847
- newState.opacity === previousStateRef.current?.opacity;
848
-
849
- if (!isOpacityOnlyChange && !isPlayStateOnlyChange && lastProcessedState.current === stateKey) {
850
- previousStateRef.current = newState;
851
- return;
852
- }
853
-
854
- if (!isOpacityOnlyChange && !isPlayStateOnlyChange) {
855
- lastProcessedState.current = stateKey;
856
- }
857
-
858
- if (isOpacityOnlyChange) {
859
- setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
860
- previousStateRef.current = newState;
861
- return;
862
- }
863
-
864
- if (isPlayStateOnlyChange) {
865
- previousStateRef.current = newState;
866
- return;
867
- }
868
-
869
- const isUnitsOnlyChange =
870
- hasInitialLoad.current &&
871
- newState.model === previousStateRef.current.model &&
872
- newState.isMRMS === previousStateRef.current.isMRMS &&
873
- newState.variable === previousStateRef.current.variable &&
874
- newState.date === previousStateRef.current.date &&
875
- newState.run === previousStateRef.current.run &&
876
- newState.forecastHour === previousStateRef.current.forecastHour &&
877
- newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
878
- newState.units !== previousStateRef.current.units;
879
-
880
- if (isUnitsOnlyChange) {
881
- const { variable, units, isMRMS, mrmsTimestamp, model, date, run, forecastHour } = newState;
882
- const oldCacheKey = isMRMS
883
- ? `mrms-${mrmsTimestamp}-${variable}`
884
- : `${model}-${date}-${run}-${forecastHour}-${variable}`;
885
-
886
- const cachedData = preloadedDataCache.current.get(oldCacheKey);
887
-
888
- if (cachedData && cachedData.originalScale !== undefined && cachedData.originalOffset !== undefined) {
889
- const { baseUnit } = core._getColormapForVariable(variable);
890
- const toUnit = core._getTargetUnit(baseUnit, units);
891
- const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
892
- const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
893
-
894
- let dataScale = cachedData.originalScale;
895
- let dataOffset = cachedData.originalOffset;
896
-
897
- if (serverDataUnit !== baseUnit) {
898
- const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
899
- if (conversionFunc) {
900
- if (cachedData.scaleType === 'sqrt') {
901
- const physicalAtOffset = dataOffset * dataOffset;
902
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
903
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
904
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
905
- const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
906
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
907
- dataScale = newOffsetPlusScale - newOffset;
908
- dataOffset = newOffset;
909
- } else {
910
- const convertedOffset = conversionFunc(dataOffset);
911
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
912
- dataScale = convertedOffsetPlusScale - convertedOffset;
913
- dataOffset = convertedOffset;
914
- }
915
- }
916
- }
917
-
918
- if (baseUnit !== toUnit) {
919
- const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
920
- if (conversionFunc) {
921
- if (cachedData.scaleType === 'sqrt') {
922
- const physicalAtOffset = dataOffset * dataOffset;
923
- const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
924
- const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
925
- const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
926
- const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
927
- const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
928
- dataScale = newOffsetPlusScale - newOffset;
929
- dataOffset = newOffset;
930
- } else {
931
- const convertedOffset = conversionFunc(dataOffset);
932
- const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
933
- dataScale = convertedOffsetPlusScale - convertedOffset;
934
- dataOffset = convertedOffset;
935
- }
936
- }
937
- }
938
-
939
- const { colormap } = core._getColormapForVariable(variable);
940
- const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
941
- let dataRange = (variable === 'ptypeRefl' || variable === 'ptypeRate') ? [5, 380] : [finalColormap[0], finalColormap[finalColormap.length - 2]];
942
- const colormapBytes = _generateColormapBytes(finalColormap);
943
- const colormapAsBase64 = fromByteArray(colormapBytes);
944
-
945
- gridLayerRef.current.updateColormapTexture(colormapAsBase64);
946
- cachedColormap.current = { key: `${variable}-${units}` };
947
- cachedDataRange.current = dataRange;
948
- setRenderProps(prev => ({ ...prev, dataRange, opacity: newState.opacity }));
949
-
950
- if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
951
- const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
952
- gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue);
953
- }
954
-
955
- const newCacheKey = isMRMS ? `mrms-${mrmsTimestamp}-${variable}` : `${model}-${date}-${run}-${forecastHour}-${variable}`;
956
- preloadedDataCache.current.set(newCacheKey, { ...cachedData, scale: dataScale, offset: dataOffset });
957
- }
958
-
959
- previousStateRef.current = newState;
960
- return;
961
- }
962
-
963
- const needsFullLoad =
964
- !hasInitialLoad.current ||
965
- newState.model !== previousStateRef.current.model ||
966
- newState.isMRMS !== previousStateRef.current.isMRMS ||
967
- newState.variable !== previousStateRef.current.variable ||
968
- newState.date !== previousStateRef.current.date ||
969
- newState.run !== previousStateRef.current.run;
970
-
971
- if (needsFullLoad) {
972
- if (gridLayerRef.current) {
973
- gridLayerRef.current.setVariable(newState.variable);
974
- gridLayerRef.current.clear();
975
- if (Platform.OS === 'ios' && gridLayerRef.current.clearGpuCache) {
976
- gridLayerRef.current.clearGpuCache();
977
- }
978
- }
979
- hasPreloadedRef.current = false;
980
- preloadedDataCache.current.clear();
981
- cachedGeometry.current = null;
982
- cachedColormap.current = null;
983
- currentGridDataRef.current = null;
984
- WeatherFrameProcessorModule.cancelAllFrames();
985
-
986
- if (!newState.variable) {
987
- previousStateRef.current = newState;
988
- return;
989
- }
990
- preloadAllFramesToDisk(newState);
991
- } else if (newState.forecastHour !== previousStateRef.current.forecastHour || (newState.isMRMS && newState.mrmsTimestamp !== previousStateRef.current.mrmsTimestamp)) {
992
- const success = updateGPUWithCachedData(newState);
993
- if (success && newState.opacity !== renderProps.opacity) {
994
- setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
995
- }
996
- }
997
-
998
- previousStateRef.current = newState;
999
- };
1000
-
1001
- handleStateChangeRef.current = handleStateChange;
1002
-
1003
- const stableHandler = (newState) => {
1004
- // OPTIMIZATION: If playing (high speed), prioritize MAP update and skip debounce
1005
- if (newState.isPlaying) {
1006
- // 1. Update Map FIRST (Native Enqueue)
1007
- if (handleStateChangeRef.current) {
1008
- handleStateChangeRef.current(newState);
1009
- }
1010
-
1011
- // 2. Update UI Slider SECOND
1012
- // This ensures the heavy map frame is processing while React reconciles the slider
1013
- props.onStateChange?.(newState);
1014
-
1015
- if (debounceTimeoutRef.current) {
1016
- clearTimeout(debounceTimeoutRef.current);
1017
- debounceTimeoutRef.current = null;
1018
- }
1019
- return;
1020
- }
1021
-
1022
- // --- Existing Logic for scrubbing/paused ---
1023
-
1024
- // 1. Immediate Slider Update for responsiveness
1025
- props.onStateChange?.(newState);
1026
-
1027
- if (debounceTimeoutRef.current) {
1028
- clearTimeout(debounceTimeoutRef.current);
1029
- }
1030
-
1031
- // Opacity and Play state changes should be immediate for the native layer too
1032
- const isOpacityOnlyChange =
1033
- previousStateRef.current &&
1034
- newState.opacity !== previousStateRef.current.opacity &&
1035
- newState.variable === previousStateRef.current.variable &&
1036
- newState.forecastHour === previousStateRef.current.forecastHour &&
1037
- newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
1038
- newState.model === previousStateRef.current.model &&
1039
- newState.units === previousStateRef.current.units;
1040
-
1041
- const isPlayStateOnlyChange =
1042
- previousStateRef.current &&
1043
- newState.isPlaying !== previousStateRef.current.isPlaying &&
1044
- newState.variable === previousStateRef.current.variable &&
1045
- newState.forecastHour === previousStateRef.current.forecastHour &&
1046
- newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
1047
- newState.model === previousStateRef.current.model &&
1048
- newState.units === previousStateRef.current.units;
1049
-
1050
- if (isOpacityOnlyChange || isPlayStateOnlyChange || !previousStateRef.current) {
1051
- if (handleStateChangeRef.current) {
1052
- handleStateChangeRef.current(newState);
1053
- }
1054
- return;
1055
- }
1056
-
1057
- debounceTimeoutRef.current = setTimeout(() => {
1058
- if (handleStateChangeRef.current) {
1059
- handleStateChangeRef.current(newState);
1060
- }
1061
- debounceTimeoutRef.current = null;
1062
- }, 16); // ~60fps map updates
1063
- };
1064
-
1065
- core.on('state:change', stableHandler);
1066
-
1067
- return () => {
1068
- core.off('state:change', stableHandler);
1069
- if (debounceTimeoutRef.current) {
1070
- clearTimeout(debounceTimeoutRef.current);
1071
- }
1072
- };
1073
- }, [core]);
1074
-
1075
- useEffect(() => {
1076
- return () => {
1077
- preloadedDataCache.current.clear(); // This drops JS references
1078
- hasInitialLoad.current = false;
1079
- lastProcessedState.current = null;
1080
- // Native cleanup
1081
- if (gridLayerRef.current && Platform.OS === 'ios') {
1082
- gridLayerRef.current.clearGpuCache();
1083
- }
1084
- };
1085
- }, []);
1086
-
1087
- const lastInspectorUpdateRef = useRef(0);
1088
- const INSPECTOR_THROTTLE_MS = 50;
1089
-
1090
- useEffect(() => {
1091
- if (!core || !inspectorEnabled) {
1092
- return;
1093
- }
1094
-
1095
- const handleMapMove = async (center) => {
1096
- if (!center || !Array.isArray(center) || center.length !== 2) {
1097
- return;
1098
- }
1099
-
1100
- // Throttle updates
1101
- const now = Date.now();
1102
- if (now - lastInspectorUpdateRef.current < INSPECTOR_THROTTLE_MS) {
1103
- return;
1104
- }
1105
- lastInspectorUpdateRef.current = now;
1106
-
1107
- const [longitude, latitude] = center;
1108
-
1109
- const payload = await getValueAtPoint(longitude, latitude);
1110
- onInspect?.(payload);
1111
- };
1112
-
1113
- core.on('map:move', handleMapMove);
1114
-
1115
- if (context && context.getCenter) {
1116
- const center = context.getCenter();
1117
- if (center) {
1118
- handleMapMove(center);
1119
- }
1120
- }
1121
-
1122
- return () => {
1123
- core.off('map:move', handleMapMove);
1124
- };
1125
- }, [inspectorEnabled, onInspect, core, context]);
1126
-
1127
- useEffect(() => {
1128
- if (!core || !inspectorEnabled) {
1129
- return;
1130
- }
1131
-
1132
- const triggerReinspection = () => {
1133
- const mapRef = mapRegistry.getMap();
1134
- const center = mapRef?._currentCenter;
1135
-
1136
- if (center && Array.isArray(center) && center.length === 2) {
1137
- const [longitude, latitude] = center;
1138
-
1139
- getValueAtPoint(longitude, latitude).then(payload => {
1140
- onInspect?.(payload);
1141
- });
1142
- }
1143
- };
1144
-
1145
- // Small delay to ensure data is loaded before re-inspecting
1146
- const timer = setTimeout(triggerReinspection, 100);
1147
-
1148
- return () => clearTimeout(timer);
1149
- }, [
1150
- core?.state?.variable,
1151
- core?.state?.model,
1152
- core?.state?.forecastHour,
1153
- core?.state?.mrmsTimestamp,
1154
- core?.state?.units,
1155
- inspectorEnabled,
1156
- onInspect
1157
- ]);
1158
-
1159
- useEffect(() => {
1160
- if (!core) {
1161
- return;
1162
- }
1163
-
1164
- const handleCameraChange = (center) => {
1165
- if (core && center) {
1166
- core.setMapCenter(center);
1167
- }
1168
- };
1169
-
1170
- // Register with the global registry
1171
- mapRegistry.addCameraListener(handleCameraChange);
1172
-
1173
- // Try to get initial center
1174
- const mapRef = mapRegistry.getMap();
1175
- if (mapRef?._currentCenter) {
1176
- handleCameraChange(mapRef._currentCenter);
1177
- }
1178
-
1179
- return () => {
1180
- mapRegistry.removeCameraListener(handleCameraChange);
1181
- };
1182
- }, [core]);
1183
-
1184
- useEffect(() => {
1185
- core.initialize();
1186
- return () => {
1187
- core.destroy();
1188
- };
1189
- }, [core]);
1190
-
1191
- return (
1192
- <GridRenderLayer
1193
- ref={gridLayerRef}
1194
- opacity={renderProps.opacity}
1195
- dataRange={renderProps.dataRange}
1196
- belowID="AML_-_terrain"
1197
- />
1198
- );
1199
- });
1200
-
1201
- WeatherLayerManager.getAvailableVariables = (options) => {
1202
- if (!options || !options.apiKey) {
1203
- console.error("API key must be provided to get available variables.");
1204
- return [];
1205
- }
1206
- const core = new AguaceroCore({ apiKey: options.apiKey });
1207
- return core.getAvailableVariables('mrms');
1
+ // packages/react-native/src/WeatherLayerManager.js
2
+
3
+ import { AguaceroCore, DICTIONARIES, getUnitConversionFunction } from '@aguacerowx/javascript-sdk';
4
+ import { fromByteArray } from 'base64-js';
5
+ import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
6
+ import { NativeModules, Platform } from 'react-native';
7
+ import { AguaceroContext } from './AguaceroContext';
8
+ import { GridRenderLayer } from './GridRenderLayer';
9
+ import { mapRegistry } from './MapRegistry';
10
+
11
+ // --- PERFORMANCE HELPER ---
12
+ const perfLog = (msg) => {
13
+ // Enable this if you want JS logs alongside native logs
14
+ console.log(`⚡️ [PERF] [JS] ${msg} | ${performance.now().toFixed(2)}ms`);
15
+ };
16
+ // --------------------------
17
+
18
+ function findLatestModelRun(modelsData, modelName) {
19
+ const model = modelsData?.[modelName];
20
+ if (!model) return null;
21
+ const availableDates = Object.keys(model).sort((a, b) => b.localeCompare(a));
22
+ for (const date of availableDates) {
23
+ const runs = model[date];
24
+ if (!runs) continue;
25
+ const availableRuns = Object.keys(runs).sort((a, b) => b.localeCompare(a));
26
+ if (availableRuns.length > 0) return { date: date, run: availableRuns[0] };
27
+ }
28
+ return null;
29
+ }
30
+ const { WeatherFrameProcessorModule, InspectorModule } = NativeModules;
31
+
32
+ /**
33
+ * A helper function to generate the raw RGBA byte buffer for the colormap texture.
34
+ */
35
+ const _generateColormapBytes = (colormap) => {
36
+ const width = 256;
37
+ const data = new Uint8Array(width * 4);
38
+ const stops = colormap.reduce((acc, _, i) => (i % 2 === 0 ? [...acc, { value: colormap[i], color: colormap[i + 1] }] : acc), []);
39
+
40
+ if (stops.length === 0) return data;
41
+
42
+ const minVal = stops[0].value;
43
+ const maxVal = stops[stops.length - 1].value;
44
+
45
+ const hexToRgb = (hex) => {
46
+ const r = parseInt(hex.slice(1, 3), 16);
47
+ const g = parseInt(hex.slice(3, 5), 16);
48
+ const b = parseInt(hex.slice(5, 7), 16);
49
+ return [r, g, b];
50
+ };
51
+
52
+ for (let i = 0; i < width; i++) {
53
+ const val = minVal + (i / (width - 1)) * (maxVal - minVal);
54
+ let lower = stops[0];
55
+ let upper = stops[stops.length - 1];
56
+ for (let j = 0; j < stops.length - 1; j++) {
57
+ if (val >= stops[j].value && val <= stops[j + 1].value) {
58
+ lower = stops[j];
59
+ upper = stops[j + 1];
60
+ break;
61
+ }
62
+ }
63
+ const t = (val - lower.value) / (upper.value - lower.value || 1);
64
+ const lowerRgb = hexToRgb(lower.color);
65
+ const upperRgb = hexToRgb(upper.color);
66
+ const rgb = lowerRgb.map((c, idx) => c * (1 - t) + upperRgb[idx] * t);
67
+
68
+ const offset = i * 4;
69
+ data[offset + 0] = Math.round(rgb[0]);
70
+ data[offset + 1] = Math.round(rgb[1]);
71
+ data[offset + 2] = Math.round(rgb[2]);
72
+ data[offset + 3] = 255;
73
+ }
74
+ return data;
75
+ };
76
+
77
+ AguaceroCore.prototype.setMapCenter = function (center) {
78
+ this.emit('map:move', center);
79
+ };
80
+
81
+ export const WeatherLayerManager = forwardRef((props, ref) => {
82
+ const {
83
+ inspectorEnabled,
84
+ onInspect,
85
+ apiKey,
86
+ customColormaps,
87
+ initialMode,
88
+ initialVariable,
89
+ autoRefresh,
90
+ autoRefreshInterval,
91
+ initialModel,
92
+ ...restProps
93
+ } = props;
94
+ const context = useContext(AguaceroContext);
95
+
96
+ // Create the core here instead of getting it from context
97
+ const core = useMemo(() => new AguaceroCore({
98
+ apiKey: apiKey,
99
+ customColormaps: customColormaps,
100
+ // ADD: Pass layerOptions to the core's constructor
101
+ layerOptions: {
102
+ mode: initialMode,
103
+ variable: initialVariable,
104
+ model: initialModel
105
+ }
106
+ }), [apiKey]);
107
+
108
+ const gridLayerRef = useRef(null);
109
+ const currentGridDataRef = useRef(null);
110
+ const autoRefreshIntervalId = useRef(null);
111
+
112
+ // Cache for preloaded grid data - stores the processed data ready for GPU upload
113
+ const preloadedDataCache = useRef(new Map());
114
+
115
+ // Store geometry and colormap that don't change with forecast hour
116
+ const cachedGeometry = useRef(null);
117
+ const cachedColormap = useRef(null);
118
+ const cachedDataRange = useRef([0, 1]);
119
+
120
+ // Track if we've done the initial load
121
+ const hasInitialLoad = useRef(false);
122
+ const hasPreloadedRef = useRef(false);
123
+
124
+ // Track the last state we processed to avoid redundant updates
125
+ const lastProcessedState = useRef(null);
126
+ const previousStateRef = useRef(null);
127
+
128
+ const [renderProps, setRenderProps] = useState({
129
+ opacity: 1,
130
+ dataRange: [0, 1]
131
+ });
132
+
133
+ useImperativeHandle(ref, () => {
134
+ const setAutoRefresh = (enabled, intervalSeconds) => {
135
+ if (autoRefreshIntervalId.current) {
136
+ clearInterval(autoRefreshIntervalId.current);
137
+ autoRefreshIntervalId.current = null;
138
+ }
139
+ if (enabled) {
140
+ const effectiveInterval = (intervalSeconds || autoRefreshInterval || 30) * 1000;
141
+ // Run once immediately, then start the interval
142
+ _checkForUpdates();
143
+ autoRefreshIntervalId.current = setInterval(_checkForUpdates, effectiveInterval);
144
+ }
145
+ };
146
+ return {
147
+ play: () => {
148
+ core.play();
149
+ },
150
+ pause: () => {
151
+ core.pause();
152
+ },
153
+ togglePlay: () => {
154
+ core.togglePlay();
155
+ },
156
+ step: (direction) => {
157
+ core.step(direction);
158
+ },
159
+ setPlaybackSpeed: (speed) => {
160
+ if (speed > 0) {
161
+ core.playbackSpeed = speed;
162
+ if (core.isPlaying) {
163
+ core.pause();
164
+ core.play();
165
+ }
166
+ }
167
+ },
168
+ setOpacity: (opacity) => core.setOpacity(opacity),
169
+ setUnits: (units) => core.setUnits(units),
170
+ switchMode: (options) => core.switchMode(options),
171
+ getAvailableVariables: (model) => core.getAvailableVariables(model),
172
+ getVariableDisplayName: (code) => core.getVariableDisplayName(code),
173
+ setRun: (runString) => core.setState({ run: runString.split(':')[1] }),
174
+ setState: (newState) => core.setState(newState),
175
+ setMRMSTimestamp: (timestamp) => core.setMRMSTimestamp(timestamp),
176
+ setSmoothing: (enabled) => {
177
+ if (gridLayerRef.current) {
178
+ gridLayerRef.current.setSmoothing(enabled);
179
+ }
180
+ },
181
+ setAutoRefresh,
182
+ refreshData: () => {
183
+ _checkForUpdates();
184
+ },
185
+ };
186
+ }, [core, autoRefreshInterval, _checkForUpdates]);
187
+
188
+ const preloadAllFramesToDisk = async (state) => {
189
+ const start = performance.now();
190
+ perfLog("preloadAllFramesToDisk called");
191
+
192
+ if (hasPreloadedRef.current) {
193
+ return;
194
+ }
195
+
196
+ const { isMRMS, model, date, run, variable, units, availableHours, availableTimestamps, forecastHour, mrmsTimestamp } = state;
197
+
198
+ // CRITICAL: Don't start preloading if we don't have a valid current frame
199
+ if (isMRMS && (mrmsTimestamp == null || !availableTimestamps || availableTimestamps.length === 0)) {
200
+ hasPreloadedRef.current = false;
201
+ return;
202
+ }
203
+
204
+ if (!isMRMS && (forecastHour == null || !availableHours || availableHours.length === 0)) {
205
+ hasPreloadedRef.current = false;
206
+ return;
207
+ }
208
+
209
+ // Only mark as "has preloaded" after validation passes
210
+ hasPreloadedRef.current = true;
211
+
212
+ // Fix the current forecast hour if it's invalid for this variable/model combo
213
+ let effectiveForecastHour = forecastHour;
214
+ if (!isMRMS && variable === 'ptypeRefl' && model === 'hrrr' && forecastHour === 0) {
215
+ const validHours = availableHours.filter(hour => hour !== 0);
216
+ effectiveForecastHour = validHours.length > 0 ? validHours[0] : 0;
217
+ }
218
+
219
+ if (!cachedGeometry.current || !cachedColormap.current) {
220
+ const gridModel = isMRMS ? 'mrms' : model;
221
+ const { corners, gridDef } = core._getGridCornersAndDef(gridModel);
222
+ gridLayerRef.current.updateGeometry(corners, gridDef);
223
+ cachedGeometry.current = { model: gridModel, variable };
224
+
225
+ const { colormap, baseUnit } = core._getColormapForVariable(variable);
226
+ const toUnit = core._getTargetUnit(baseUnit, units);
227
+ const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
228
+ let dataRange;
229
+ if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
230
+ dataRange = isMRMS ? [5, 380] : [5, 380];
231
+ } else {
232
+ dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
233
+ }
234
+ const colormapBytes = _generateColormapBytes(finalColormap);
235
+ const colormapAsBase64 = fromByteArray(colormapBytes);
236
+
237
+ gridLayerRef.current.updateColormapTexture(colormapAsBase64);
238
+ cachedColormap.current = { key: `${variable}-${units}` };
239
+ cachedDataRange.current = dataRange;
240
+
241
+ setRenderProps({ opacity: state.opacity, dataRange: dataRange });
242
+ hasInitialLoad.current = true;
243
+ }
244
+
245
+ // Apply the same filtering logic as in AguaceroCore._emitStateChange
246
+ let filteredHours = availableHours;
247
+ if (!isMRMS && variable === 'ptypeRefl' && model === 'hrrr' && availableHours && availableHours.length > 0) {
248
+ filteredHours = availableHours.filter(hour => hour !== 0);
249
+ }
250
+
251
+ const allFrames = isMRMS ? availableTimestamps : filteredHours;
252
+ if (!allFrames || allFrames.length === 0) {
253
+ return;
254
+ }
255
+
256
+ const currentFrame = isMRMS ? mrmsTimestamp : effectiveForecastHour;
257
+
258
+ // Double-check currentFrame is valid
259
+ if (currentFrame == null) {
260
+ hasPreloadedRef.current = false;
261
+ }
262
+
263
+ // Reverse the frame order to load from last to first
264
+ const reversedFrames = [...allFrames].reverse();
265
+ const framesToPreload = reversedFrames.filter(frame => frame !== currentFrame);
266
+
267
+ const { corners, gridDef } = core._getGridCornersAndDef(isMRMS ? 'mrms' : model);
268
+ const { nx, ny } = gridDef.grid_params;
269
+
270
+ // Load the current frame FIRST and WAIT for it before continuing
271
+ const currentCacheKey = isMRMS ? `mrms-${currentFrame}-${variable}` : `${model}-${date}-${run}-${currentFrame}-${variable}`;
272
+
273
+ if (!preloadedDataCache.current.has(currentCacheKey)) {
274
+ let resourcePath;
275
+ if (isMRMS) {
276
+ const frameDate = new Date(currentFrame * 1000);
277
+ const y = frameDate.getUTCFullYear();
278
+ const m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0');
279
+ const d = frameDate.getUTCDate().toString().padStart(2, '0');
280
+ resourcePath = `/grids/mrms/${y}${m}${d}/${currentFrame}/0/${variable}/0`;
281
+ } else {
282
+ resourcePath = `/grids/${model}/${date}/${run}/${currentFrame}/${variable}/0`;
283
+ }
284
+
285
+ const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
286
+ const options = { url, apiKey: core.apiKey, bundleId: core.bundleId };
287
+
288
+ try {
289
+ const netStart = performance.now();
290
+ const result = await WeatherFrameProcessorModule.processFrame(options);
291
+ perfLog(`First frame fetched in ${(performance.now() - netStart).toFixed(2)}ms`);
292
+
293
+ if (!result || !result.filePath) {
294
+ return;
295
+ }
296
+
297
+ const { baseUnit } = core._getColormapForVariable(variable);
298
+
299
+ const toUnit = core._getTargetUnit(baseUnit, units);
300
+
301
+ const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
302
+ const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
303
+
304
+ let dataScale = result.scale;
305
+ let dataOffset = result.offset;
306
+
307
+ let convertedScale = dataScale;
308
+ let convertedOffset = dataOffset;
309
+
310
+ if (serverDataUnit !== baseUnit) {
311
+ const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
312
+ if (conversionFunc) {
313
+ if (result.scaleType === 'sqrt') {
314
+ const physicalAtOffset = dataOffset * dataOffset;
315
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
316
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
317
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
318
+ convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
319
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
320
+ convertedScale = newOffsetPlusScale - convertedOffset;
321
+ } else {
322
+ convertedOffset = conversionFunc(dataOffset);
323
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
324
+ convertedScale = convertedOffsetPlusScale - convertedOffset;
325
+ }
326
+ dataScale = convertedScale;
327
+ dataOffset = convertedOffset;
328
+ }
329
+ }
330
+
331
+ if (baseUnit !== toUnit) {
332
+ const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
333
+ if (conversionFunc) {
334
+ if (result.scaleType === 'sqrt') {
335
+ const physicalAtOffset = dataOffset * dataOffset;
336
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
337
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
338
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
339
+ convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
340
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
341
+ convertedScale = newOffsetPlusScale - convertedOffset;
342
+ } else {
343
+ convertedOffset = conversionFunc(dataOffset);
344
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
345
+ convertedScale = convertedOffsetPlusScale - convertedOffset;
346
+ }
347
+ }
348
+ }
349
+
350
+ const frameData = {
351
+ filePath: result.filePath,
352
+ nx, ny,
353
+ scale: convertedScale,
354
+ offset: convertedOffset,
355
+ missing: result.missing,
356
+ corners,
357
+ gridDef,
358
+ scaleType: result.scaleType,
359
+ originalScale: result.scale,
360
+ originalOffset: result.offset
361
+ };
362
+
363
+ preloadedDataCache.current.set(currentCacheKey, frameData);
364
+
365
+ // Update the GPU with the current frame
366
+ gridLayerRef.current.updateDataTextureFromFile(
367
+ frameData.filePath,
368
+ frameData.nx, frameData.ny,
369
+ frameData.scale, frameData.offset, frameData.missing,
370
+ frameData.scaleType
371
+ );
372
+
373
+ perfLog(`First frame updated to GPU`);
374
+
375
+ currentGridDataRef.current = {
376
+ nx: frameData.nx,
377
+ ny: frameData.ny,
378
+ scale: frameData.scale,
379
+ offset: frameData.offset,
380
+ missing: frameData.missing,
381
+ gridDef: frameData.gridDef,
382
+ variable: variable,
383
+ units: units,
384
+ scaleType: frameData.scaleType
385
+ };
386
+ } catch (error) {
387
+ console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${currentFrame}:`, error);
388
+ hasPreloadedRef.current = false;
389
+ }
390
+ }
391
+
392
+ // NOW preload the rest of the frames asynchronously
393
+ perfLog(`Triggering fetch for ${framesToPreload.length} background frames`);
394
+
395
+ framesToPreload.forEach((frame) => {
396
+ const cacheKey = isMRMS ? `mrms-${frame}-${variable}` : `${model}-${date}-${run}-${frame}-${variable}`;
397
+ if (preloadedDataCache.current.has(cacheKey)) {
398
+ return;
399
+ }
400
+
401
+ let resourcePath;
402
+ if (isMRMS) {
403
+ const frameDate = new Date(frame * 1000);
404
+ const y = frameDate.getUTCFullYear();
405
+ const m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0');
406
+ const d = frameDate.getUTCDate().toString().padStart(2, '0');
407
+ resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${variable}/0`;
408
+ } else {
409
+ resourcePath = `/grids/${model}/${date}/${run}/${frame}/${variable}/0`;
410
+ }
411
+
412
+ const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
413
+ const options = { url, apiKey: core.apiKey, bundleId: core.bundleId };
414
+
415
+ WeatherFrameProcessorModule.processFrame(options)
416
+ .then(result => {
417
+ if (!result || !result.filePath) {
418
+ console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${frame}: No filePath`);
419
+ return;
420
+ }
421
+
422
+ // ADD: Same two-step conversion as the current frame
423
+ const { baseUnit } = core._getColormapForVariable(variable);
424
+ const toUnit = core._getTargetUnit(baseUnit, units);
425
+ const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
426
+ const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
427
+
428
+ let dataScale = result.scale;
429
+ let dataOffset = result.offset;
430
+
431
+ let convertedScale = dataScale;
432
+ let convertedOffset = dataOffset;
433
+
434
+ // Step 1: Convert from server unit to colormap base unit
435
+ if (serverDataUnit !== baseUnit) {
436
+ const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
437
+ if (conversionFunc) {
438
+ if (result.scaleType === 'sqrt') {
439
+ const physicalAtOffset = dataOffset * dataOffset;
440
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
441
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
442
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
443
+ convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
444
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
445
+ convertedScale = newOffsetPlusScale - convertedOffset;
446
+ } else {
447
+ convertedOffset = conversionFunc(dataOffset);
448
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
449
+ convertedScale = convertedOffsetPlusScale - convertedOffset;
450
+ }
451
+ dataScale = convertedScale;
452
+ dataOffset = convertedOffset;
453
+ }
454
+ }
455
+
456
+ // Step 2: Convert from colormap base unit to target display unit
457
+ if (baseUnit !== toUnit) {
458
+ const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
459
+ if (conversionFunc) {
460
+ if (result.scaleType === 'sqrt') {
461
+ const physicalAtOffset = dataOffset * dataOffset;
462
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
463
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
464
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
465
+ convertedOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
466
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
467
+ convertedScale = newOffsetPlusScale - convertedOffset;
468
+ } else {
469
+ convertedOffset = conversionFunc(dataOffset);
470
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
471
+ convertedScale = convertedOffsetPlusScale - convertedOffset;
472
+ }
473
+ }
474
+ }
475
+
476
+ const frameData = {
477
+ filePath: result.filePath,
478
+ nx, ny,
479
+ scale: convertedScale,
480
+ offset: convertedOffset,
481
+ missing: result.missing,
482
+ corners,
483
+ gridDef,
484
+ scaleType: result.scaleType,
485
+ originalScale: result.scale,
486
+ originalOffset: result.offset
487
+ };
488
+
489
+ preloadedDataCache.current.set(cacheKey, frameData);
490
+
491
+ if (Platform.OS === 'ios' && gridLayerRef.current.primeGpuCache) {
492
+ const frameInfoForGpu = {
493
+ [cacheKey]: {
494
+ filePath: frameData.filePath,
495
+ nx: frameData.nx,
496
+ ny: frameData.ny,
497
+ scale: frameData.scale,
498
+ offset: frameData.offset,
499
+ missing: frameData.missing,
500
+ scaleType: frameData.scaleType || 'linear',
501
+ originalScale: frameData.originalScale,
502
+ originalOffset: frameData.originalOffset
503
+ }
504
+ };
505
+ gridLayerRef.current.primeGpuCache(frameInfoForGpu);
506
+ }
507
+ })
508
+ .catch(error => {
509
+ console.warn(`⚠️ [preloadAllFramesToDisk] Failed frame ${frame}:`, error);
510
+ });
511
+ });
512
+
513
+ perfLog(`Completed preload triggering in ${(performance.now() - start).toFixed(2)}ms`);
514
+ };
515
+
516
+ useEffect(() => {
517
+ // This effect manages the auto-refresh based on props
518
+ if (autoRefresh) {
519
+ const interval = (autoRefreshInterval || 30) * 1000;
520
+ _checkForUpdates(); // Run immediately on enable
521
+ autoRefreshIntervalId.current = setInterval(_checkForUpdates, interval);
522
+ }
523
+
524
+ // Cleanup function: this runs when the component unmounts or props change
525
+ return () => {
526
+ if (autoRefreshIntervalId.current) {
527
+ clearInterval(autoRefreshIntervalId.current);
528
+ autoRefreshIntervalId.current = null;
529
+ }
530
+ };
531
+ }, [autoRefresh, autoRefreshInterval, _checkForUpdates]);
532
+
533
+ const updateGPUWithCachedData = (state) => {
534
+ const start = performance.now();
535
+ const { model, date, run, forecastHour, variable, units, isMRMS, mrmsTimestamp } = state;
536
+
537
+ const cacheKey = isMRMS
538
+ ? `mrms-${mrmsTimestamp}-${variable}`
539
+ : `${model}-${date}-${run}-${forecastHour}-${variable}`;
540
+
541
+ if (Platform.OS === 'ios' && gridLayerRef.current.setActiveFrame) {
542
+ // Get the cached data BEFORE calling setActiveFrame
543
+ const cachedData = preloadedDataCache.current.get(cacheKey);
544
+
545
+ if (cachedData) {
546
+ currentGridDataRef.current = {
547
+ nx: cachedData.nx,
548
+ ny: cachedData.ny,
549
+ scale: cachedData.scale,
550
+ offset: cachedData.offset,
551
+ missing: cachedData.missing,
552
+ gridDef: cachedData.gridDef,
553
+ variable: variable,
554
+ units: units,
555
+ scaleType: cachedData.scaleType
556
+ };
557
+ }
558
+
559
+ if (__DEV__) {
560
+ console.log(`[WeatherLayerManager] setActiveFrame: ${cacheKey}`);
561
+ }
562
+ gridLayerRef.current.setActiveFrame(cacheKey);
563
+ perfLog(`setActiveFrame sent in ${(performance.now() - start).toFixed(2)}ms`);
564
+ return true;
565
+ }
566
+
567
+ const cachedData = preloadedDataCache.current.get(cacheKey);
568
+
569
+ if (!cachedData) {
570
+ return false;
571
+ }
572
+
573
+ if (!gridLayerRef.current) {
574
+ console.warn(`⚠️ [updateGPUWithCachedData] GridLayer ref not available`); // CHANGED
575
+ return false;
576
+ }
577
+
578
+ if (!cachedGeometry.current || cachedGeometry.current.model !== (isMRMS ? 'mrms' : model) || cachedGeometry.current.variable !== variable) {
579
+ gridLayerRef.current.updateGeometry(cachedData.corners, cachedData.gridDef);
580
+ cachedGeometry.current = { model: (isMRMS ? 'mrms' : model), variable };
581
+ }
582
+
583
+ const colormapKey = `${variable}-${units}`;
584
+ if (!cachedColormap.current || cachedColormap.current.key !== colormapKey) {
585
+ const { colormap, baseUnit } = core._getColormapForVariable(variable);
586
+ const toUnit = core._getTargetUnit(baseUnit, units);
587
+ const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
588
+ let dataRange;
589
+ if (variable === 'ptypeRefl' || variable === 'ptypeRate') {
590
+ if (isMRMS) {
591
+ dataRange = [5, 380];
592
+ } else {
593
+ dataRange = [5, 380];
594
+ }
595
+ } else {
596
+ dataRange = [finalColormap[0], finalColormap[finalColormap.length - 2]];
597
+ }
598
+ const colormapBytes = _generateColormapBytes(finalColormap);
599
+ const colormapAsBase64 = fromByteArray(colormapBytes);
600
+
601
+ gridLayerRef.current.updateColormapTexture(colormapAsBase64);
602
+ cachedColormap.current = { key: colormapKey };
603
+ cachedDataRange.current = dataRange;
604
+
605
+ setRenderProps(prev => ({ ...prev, dataRange }));
606
+ }
607
+
608
+ if (cachedData.filePath) {
609
+ gridLayerRef.current.updateDataTextureFromFile(
610
+ cachedData.filePath,
611
+ cachedData.nx, cachedData.ny,
612
+ cachedData.scale, cachedData.offset, cachedData.missing,
613
+ cachedData.scaleType
614
+ );
615
+ currentGridDataRef.current = {
616
+ nx: cachedData.nx,
617
+ ny: cachedData.ny,
618
+ scale: cachedData.scale,
619
+ offset: cachedData.offset,
620
+ missing: cachedData.missing,
621
+ gridDef: cachedData.gridDef,
622
+ variable: variable,
623
+ units: units,
624
+ scaleType: cachedData.scaleType
625
+ };
626
+ } else {
627
+ // filePath should ALWAYS exist now — log and bail
628
+ console.error('❌ [updateGPUWithCachedData] No filePath in cached data. Keys:', Object.keys(cachedData));
629
+ return false;
630
+ }
631
+
632
+ // Update inspector parameters for file-based data too
633
+ if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
634
+ gridLayerRef.current.updateDataParameters(cachedData.scale, cachedData.offset, cachedData.missing);
635
+ }
636
+ return true;
637
+ };
638
+
639
+ const handleStateChangeRef = useRef(null);
640
+ const debounceTimeoutRef = useRef(null);
641
+
642
+ useEffect(() => {
643
+ if (core && props.customColormaps) {
644
+ core.customColormaps = props.customColormaps;
645
+ // Trigger a re-render if we already have data loaded
646
+ if (hasInitialLoad.current) {
647
+ core._emitStateChange();
648
+ }
649
+ }
650
+ }, [core, props.customColormaps]);
651
+
652
+ const getValueAtPoint = async (lng, lat) => {
653
+ if (!core) {
654
+ console.warn('🔍 [Inspector] Core not available');
655
+ return null;
656
+ }
657
+
658
+ // ADD THIS: Check if we have valid data before attempting inspection
659
+ if (!currentGridDataRef.current) {
660
+ return null;
661
+ }
662
+
663
+ try {
664
+ const gridIndices = core._getGridIndexFromLngLat(lng, lat);
665
+ if (!gridIndices) return null;
666
+
667
+ const { i, j } = gridIndices;
668
+
669
+ const value = await InspectorModule.getValueAtGridIndex(i, j);
670
+
671
+ if (value === null) {
672
+ return null;
673
+ }
674
+
675
+ const { colormap, baseUnit } = core._getColormapForVariable(core.state.variable);
676
+ const displayUnit = core._getTargetUnit(baseUnit, core.state.units);
677
+ const finalColormap = core._convertColormapUnits(colormap, baseUnit, displayUnit);
678
+ const minThreshold = finalColormap[0];
679
+
680
+ if (value < minThreshold) {
681
+ return null;
682
+ }
683
+
684
+ // Filter out values below the minimum threshold (matching shader behavior)
685
+ if (value < minThreshold) {
686
+ return null;
687
+ }
688
+
689
+ // Also check if value is NaN or effectively missing
690
+ if (!isFinite(value)) {
691
+ return null;
692
+ }
693
+
694
+ return {
695
+ value: value,
696
+ unit: displayUnit,
697
+ variable: {
698
+ code: core.state.variable,
699
+ name: core.getVariableDisplayName(core.state.variable)
700
+ },
701
+ lngLat: { lng, lat }
702
+ };
703
+ } catch (error) {
704
+ console.error('🔍 [Inspector] Error:', error);
705
+ return null;
706
+ }
707
+ };
708
+
709
+ const _checkForUpdates = useMemo(() => async () => {
710
+ if (!core) return;
711
+ const { isMRMS, model: currentModel, variable: currentVariable, date, run } = core.state;
712
+
713
+ if (isMRMS) {
714
+ // --- MRMS LOGIC (Keep existing logic) ---
715
+ const oldTimestamps = new Set(core.mrmsStatus?.[currentVariable] || []);
716
+ const mrmsStatus = await core.fetchMRMSStatus(true);
717
+ const newTimestamps = mrmsStatus?.[currentVariable] || [];
718
+ if (newTimestamps.length === 0) return;
719
+
720
+ const newTimestampsToPreload = newTimestamps.filter(ts => !oldTimestamps.has(ts));
721
+
722
+ if (newTimestampsToPreload.length > 0) {
723
+ core.mrmsStatus = mrmsStatus;
724
+ core._emitStateChange(); // Update UI slider without changing selection
725
+
726
+ // ... (Keep your existing preloading logic here) ...
727
+ const { corners, gridDef } = core._getGridCornersAndDef('mrms');
728
+ const { nx, ny } = gridDef.grid_params;
729
+
730
+ newTimestampsToPreload.forEach(frame => {
731
+ const cacheKey = `mrms-${frame}-${currentVariable}`;
732
+ if (preloadedDataCache.current.has(cacheKey)) return;
733
+
734
+ const frameDate = new Date(frame * 1000);
735
+ const y = frameDate.getUTCFullYear(), m = (frameDate.getUTCMonth() + 1).toString().padStart(2, '0'), d = frameDate.getUTCDate().toString().padStart(2, '0');
736
+ const resourcePath = `/grids/mrms/${y}${m}${d}/${frame}/0/${currentVariable}/0`;
737
+ const url = `${core.baseGridUrl}${resourcePath}?apiKey=${core.apiKey}`;
738
+
739
+ WeatherFrameProcessorModule.processFrame({ url, apiKey: core.apiKey, bundleId: core.bundleId })
740
+ .then(result => {
741
+ if (!result || !result.filePath) return;
742
+ const frameData = { filePath: result.filePath, nx, ny, scale: result.scale, offset: result.offset, missing: result.missing, corners, gridDef, scaleType: result.scaleType, originalScale: result.scale, originalOffset: result.offset };
743
+ preloadedDataCache.current.set(cacheKey, frameData);
744
+ if (Platform.OS === 'ios' && gridLayerRef.current?.primeGpuCache) {
745
+ gridLayerRef.current.primeGpuCache({ [cacheKey]: frameData });
746
+ }
747
+ }).catch(error => console.warn(`[Auto-Refresh] Failed to preload frame ${frame}:`, error));
748
+ });
749
+
750
+ const newTimestampsSet = new Set(newTimestamps);
751
+ oldTimestamps.forEach(oldTs => {
752
+ if (!newTimestampsSet.has(oldTs)) {
753
+ const cacheKey = `mrms-${oldTs}-${currentVariable}`;
754
+ preloadedDataCache.current.delete(cacheKey);
755
+ }
756
+ });
757
+ }
758
+ } else {
759
+ const modelStatus = await core.fetchModelStatus(true);
760
+ core._emitStateChange();
761
+
762
+ const latestRun = findLatestModelRun(modelStatus, currentModel);
763
+ if (!latestRun) return;
764
+ }
765
+ }, [core]);
766
+
767
+ useEffect(() => {
768
+ if (!core) {
769
+ console.warn('⚠️ [useEffect] Core is not available yet');
770
+ return;
771
+ }
772
+
773
+ const handleStateChange = (newState) => {
774
+ if (!previousStateRef.current) {
775
+ previousStateRef.current = core.state;
776
+ }
777
+
778
+ const variableChanged = !previousStateRef.current || newState.variable !== previousStateRef.current.variable;
779
+
780
+ if (variableChanged && gridLayerRef.current?.setVariable) {
781
+ gridLayerRef.current.setVariable(newState.variable);
782
+ }
783
+
784
+ const stateKey = `${newState.model}-${newState.variable}-${newState.date}-${newState.run}-${newState.forecastHour}-${newState.units}-${newState.mrmsTimestamp}`;
785
+
786
+ const isOpacityOnlyChange =
787
+ hasInitialLoad.current &&
788
+ newState.opacity !== renderProps.opacity &&
789
+ newState.variable === previousStateRef.current?.variable &&
790
+ newState.forecastHour === previousStateRef.current?.forecastHour &&
791
+ newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp &&
792
+ newState.model === previousStateRef.current?.model &&
793
+ newState.units === previousStateRef.current?.units;
794
+
795
+ const isPlayStateOnlyChange =
796
+ hasInitialLoad.current &&
797
+ newState.isPlaying !== previousStateRef.current?.isPlaying &&
798
+ newState.variable === previousStateRef.current?.variable &&
799
+ newState.forecastHour === previousStateRef.current?.forecastHour &&
800
+ newState.mrmsTimestamp === previousStateRef.current?.mrmsTimestamp &&
801
+ newState.model === previousStateRef.current?.model &&
802
+ newState.units === previousStateRef.current?.units &&
803
+ newState.opacity === previousStateRef.current?.opacity;
804
+
805
+ if (!isOpacityOnlyChange && !isPlayStateOnlyChange && lastProcessedState.current === stateKey) {
806
+ previousStateRef.current = newState;
807
+ return;
808
+ }
809
+
810
+ if (!isOpacityOnlyChange && !isPlayStateOnlyChange) {
811
+ lastProcessedState.current = stateKey;
812
+ }
813
+
814
+ if (isOpacityOnlyChange) {
815
+ setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
816
+ previousStateRef.current = newState;
817
+ return;
818
+ }
819
+
820
+ if (isPlayStateOnlyChange) {
821
+ previousStateRef.current = newState;
822
+ return;
823
+ }
824
+
825
+ const isUnitsOnlyChange =
826
+ hasInitialLoad.current &&
827
+ newState.model === previousStateRef.current.model &&
828
+ newState.isMRMS === previousStateRef.current.isMRMS &&
829
+ newState.variable === previousStateRef.current.variable &&
830
+ newState.date === previousStateRef.current.date &&
831
+ newState.run === previousStateRef.current.run &&
832
+ newState.forecastHour === previousStateRef.current.forecastHour &&
833
+ newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
834
+ newState.units !== previousStateRef.current.units;
835
+
836
+ if (isUnitsOnlyChange) {
837
+ const { variable, units, isMRMS, mrmsTimestamp, model, date, run, forecastHour } = newState;
838
+ const oldCacheKey = isMRMS
839
+ ? `mrms-${mrmsTimestamp}-${variable}`
840
+ : `${model}-${date}-${run}-${forecastHour}-${variable}`;
841
+
842
+ const cachedData = preloadedDataCache.current.get(oldCacheKey);
843
+
844
+ if (cachedData && cachedData.originalScale !== undefined && cachedData.originalOffset !== undefined) {
845
+ const { baseUnit } = core._getColormapForVariable(variable);
846
+ const toUnit = core._getTargetUnit(baseUnit, units);
847
+ const fieldInfo = DICTIONARIES?.fld?.[variable] || {};
848
+ const serverDataUnit = fieldInfo.defaultUnit || baseUnit;
849
+
850
+ let dataScale = cachedData.originalScale;
851
+ let dataOffset = cachedData.originalOffset;
852
+
853
+ if (serverDataUnit !== baseUnit) {
854
+ const conversionFunc = getUnitConversionFunction(serverDataUnit, baseUnit);
855
+ if (conversionFunc) {
856
+ if (cachedData.scaleType === 'sqrt') {
857
+ const physicalAtOffset = dataOffset * dataOffset;
858
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
859
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
860
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
861
+ const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
862
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
863
+ dataScale = newOffsetPlusScale - newOffset;
864
+ dataOffset = newOffset;
865
+ } else {
866
+ const convertedOffset = conversionFunc(dataOffset);
867
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
868
+ dataScale = convertedOffsetPlusScale - convertedOffset;
869
+ dataOffset = convertedOffset;
870
+ }
871
+ }
872
+ }
873
+
874
+ if (baseUnit !== toUnit) {
875
+ const conversionFunc = getUnitConversionFunction(baseUnit, toUnit);
876
+ if (conversionFunc) {
877
+ if (cachedData.scaleType === 'sqrt') {
878
+ const physicalAtOffset = dataOffset * dataOffset;
879
+ const physicalAtOffsetPlusScale = (dataOffset + dataScale) * (dataOffset + dataScale);
880
+ const convertedPhysicalAtOffset = conversionFunc(physicalAtOffset);
881
+ const convertedPhysicalAtOffsetPlusScale = conversionFunc(physicalAtOffsetPlusScale);
882
+ const newOffset = Math.sqrt(Math.abs(convertedPhysicalAtOffset)) * Math.sign(convertedPhysicalAtOffset);
883
+ const newOffsetPlusScale = Math.sqrt(Math.abs(convertedPhysicalAtOffsetPlusScale)) * Math.sign(convertedPhysicalAtOffsetPlusScale);
884
+ dataScale = newOffsetPlusScale - newOffset;
885
+ dataOffset = newOffset;
886
+ } else {
887
+ const convertedOffset = conversionFunc(dataOffset);
888
+ const convertedOffsetPlusScale = conversionFunc(dataOffset + dataScale);
889
+ dataScale = convertedOffsetPlusScale - convertedOffset;
890
+ dataOffset = convertedOffset;
891
+ }
892
+ }
893
+ }
894
+
895
+ const { colormap } = core._getColormapForVariable(variable);
896
+ const finalColormap = core._convertColormapUnits(colormap, baseUnit, toUnit);
897
+ let dataRange = (variable === 'ptypeRefl' || variable === 'ptypeRate') ? [5, 380] : [finalColormap[0], finalColormap[finalColormap.length - 2]];
898
+ const colormapBytes = _generateColormapBytes(finalColormap);
899
+ const colormapAsBase64 = fromByteArray(colormapBytes);
900
+
901
+ gridLayerRef.current.updateColormapTexture(colormapAsBase64);
902
+ cachedColormap.current = { key: `${variable}-${units}` };
903
+ cachedDataRange.current = dataRange;
904
+ setRenderProps(prev => ({ ...prev, dataRange, opacity: newState.opacity }));
905
+
906
+ if (gridLayerRef.current && gridLayerRef.current.updateDataParameters) {
907
+ const scaleTypeValue = cachedData.scaleType === 'sqrt' ? 1 : 0;
908
+ gridLayerRef.current.updateDataParameters(dataScale, dataOffset, cachedData.missing, scaleTypeValue);
909
+ }
910
+
911
+ const newCacheKey = isMRMS ? `mrms-${mrmsTimestamp}-${variable}` : `${model}-${date}-${run}-${forecastHour}-${variable}`;
912
+ preloadedDataCache.current.set(newCacheKey, { ...cachedData, scale: dataScale, offset: dataOffset });
913
+ }
914
+
915
+ previousStateRef.current = newState;
916
+ return;
917
+ }
918
+
919
+ const needsFullLoad =
920
+ !hasInitialLoad.current ||
921
+ newState.model !== previousStateRef.current.model ||
922
+ newState.isMRMS !== previousStateRef.current.isMRMS ||
923
+ newState.variable !== previousStateRef.current.variable ||
924
+ newState.date !== previousStateRef.current.date ||
925
+ newState.run !== previousStateRef.current.run;
926
+
927
+ if (needsFullLoad) {
928
+ if (gridLayerRef.current) {
929
+ gridLayerRef.current.setVariable(newState.variable);
930
+ gridLayerRef.current.clear();
931
+ if (Platform.OS === 'ios' && gridLayerRef.current.clearGpuCache) {
932
+ gridLayerRef.current.clearGpuCache();
933
+ }
934
+ }
935
+ hasPreloadedRef.current = false;
936
+ preloadedDataCache.current.clear();
937
+ cachedGeometry.current = null;
938
+ cachedColormap.current = null;
939
+ currentGridDataRef.current = null;
940
+ WeatherFrameProcessorModule.cancelAllFrames();
941
+
942
+ if (!newState.variable) {
943
+ previousStateRef.current = newState;
944
+ return;
945
+ }
946
+ preloadAllFramesToDisk(newState);
947
+ } else if (newState.forecastHour !== previousStateRef.current.forecastHour || (newState.isMRMS && newState.mrmsTimestamp !== previousStateRef.current.mrmsTimestamp)) {
948
+ const success = updateGPUWithCachedData(newState);
949
+ if (success && newState.opacity !== renderProps.opacity) {
950
+ setRenderProps(prev => ({ ...prev, opacity: newState.opacity }));
951
+ }
952
+ }
953
+
954
+ previousStateRef.current = newState;
955
+ };
956
+
957
+ handleStateChangeRef.current = handleStateChange;
958
+
959
+ const stableHandler = (newState) => {
960
+ // OPTIMIZATION: If playing (high speed), prioritize MAP update and skip debounce
961
+ if (newState.isPlaying) {
962
+ // 1. Update Map FIRST (Native Enqueue)
963
+ if (handleStateChangeRef.current) {
964
+ handleStateChangeRef.current(newState);
965
+ }
966
+
967
+ // 2. Update UI Slider SECOND
968
+ // This ensures the heavy map frame is processing while React reconciles the slider
969
+ props.onStateChange?.(newState);
970
+
971
+ if (debounceTimeoutRef.current) {
972
+ clearTimeout(debounceTimeoutRef.current);
973
+ debounceTimeoutRef.current = null;
974
+ }
975
+ return;
976
+ }
977
+
978
+ // --- Existing Logic for scrubbing/paused ---
979
+
980
+ // 1. Immediate Slider Update for responsiveness
981
+ props.onStateChange?.(newState);
982
+
983
+ if (debounceTimeoutRef.current) {
984
+ clearTimeout(debounceTimeoutRef.current);
985
+ }
986
+
987
+ // Opacity and Play state changes should be immediate for the native layer too
988
+ const isOpacityOnlyChange =
989
+ previousStateRef.current &&
990
+ newState.opacity !== previousStateRef.current.opacity &&
991
+ newState.variable === previousStateRef.current.variable &&
992
+ newState.forecastHour === previousStateRef.current.forecastHour &&
993
+ newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
994
+ newState.model === previousStateRef.current.model &&
995
+ newState.units === previousStateRef.current.units;
996
+
997
+ const isPlayStateOnlyChange =
998
+ previousStateRef.current &&
999
+ newState.isPlaying !== previousStateRef.current.isPlaying &&
1000
+ newState.variable === previousStateRef.current.variable &&
1001
+ newState.forecastHour === previousStateRef.current.forecastHour &&
1002
+ newState.mrmsTimestamp === previousStateRef.current.mrmsTimestamp &&
1003
+ newState.model === previousStateRef.current.model &&
1004
+ newState.units === previousStateRef.current.units;
1005
+
1006
+ if (isOpacityOnlyChange || isPlayStateOnlyChange || !previousStateRef.current) {
1007
+ if (handleStateChangeRef.current) {
1008
+ handleStateChangeRef.current(newState);
1009
+ }
1010
+ return;
1011
+ }
1012
+
1013
+ debounceTimeoutRef.current = setTimeout(() => {
1014
+ if (handleStateChangeRef.current) {
1015
+ handleStateChangeRef.current(newState);
1016
+ }
1017
+ debounceTimeoutRef.current = null;
1018
+ }, 16); // ~60fps map updates
1019
+ };
1020
+
1021
+ core.on('state:change', stableHandler);
1022
+
1023
+ return () => {
1024
+ core.off('state:change', stableHandler);
1025
+ if (debounceTimeoutRef.current) {
1026
+ clearTimeout(debounceTimeoutRef.current);
1027
+ }
1028
+ };
1029
+ }, [core]);
1030
+
1031
+ useEffect(() => {
1032
+ return () => {
1033
+ preloadedDataCache.current.clear(); // This drops JS references
1034
+ hasInitialLoad.current = false;
1035
+ lastProcessedState.current = null;
1036
+ // Native cleanup
1037
+ if (gridLayerRef.current && Platform.OS === 'ios') {
1038
+ gridLayerRef.current.clearGpuCache();
1039
+ }
1040
+ };
1041
+ }, []);
1042
+
1043
+ const lastInspectorUpdateRef = useRef(0);
1044
+ const INSPECTOR_THROTTLE_MS = 50;
1045
+
1046
+ useEffect(() => {
1047
+ if (!core || !inspectorEnabled) {
1048
+ return;
1049
+ }
1050
+
1051
+ const handleMapMove = async (center) => {
1052
+ if (!center || !Array.isArray(center) || center.length !== 2) {
1053
+ return;
1054
+ }
1055
+
1056
+ // Throttle updates
1057
+ const now = Date.now();
1058
+ if (now - lastInspectorUpdateRef.current < INSPECTOR_THROTTLE_MS) {
1059
+ return;
1060
+ }
1061
+ lastInspectorUpdateRef.current = now;
1062
+
1063
+ const [longitude, latitude] = center;
1064
+
1065
+ const payload = await getValueAtPoint(longitude, latitude);
1066
+ onInspect?.(payload);
1067
+ };
1068
+
1069
+ core.on('map:move', handleMapMove);
1070
+
1071
+ if (context && context.getCenter) {
1072
+ const center = context.getCenter();
1073
+ if (center) {
1074
+ handleMapMove(center);
1075
+ }
1076
+ }
1077
+
1078
+ return () => {
1079
+ core.off('map:move', handleMapMove);
1080
+ };
1081
+ }, [inspectorEnabled, onInspect, core, context]);
1082
+
1083
+ useEffect(() => {
1084
+ if (!core || !inspectorEnabled) {
1085
+ return;
1086
+ }
1087
+
1088
+ const triggerReinspection = () => {
1089
+ const mapRef = mapRegistry.getMap();
1090
+ const center = mapRef?._currentCenter;
1091
+
1092
+ if (center && Array.isArray(center) && center.length === 2) {
1093
+ const [longitude, latitude] = center;
1094
+
1095
+ getValueAtPoint(longitude, latitude).then(payload => {
1096
+ onInspect?.(payload);
1097
+ });
1098
+ }
1099
+ };
1100
+
1101
+ // Small delay to ensure data is loaded before re-inspecting
1102
+ const timer = setTimeout(triggerReinspection, 100);
1103
+
1104
+ return () => clearTimeout(timer);
1105
+ }, [
1106
+ core?.state?.variable,
1107
+ core?.state?.model,
1108
+ core?.state?.forecastHour,
1109
+ core?.state?.mrmsTimestamp,
1110
+ core?.state?.units,
1111
+ inspectorEnabled,
1112
+ onInspect
1113
+ ]);
1114
+
1115
+ useEffect(() => {
1116
+ if (!core) {
1117
+ return;
1118
+ }
1119
+
1120
+ const handleCameraChange = (center) => {
1121
+ if (core && center) {
1122
+ core.setMapCenter(center);
1123
+ }
1124
+ };
1125
+
1126
+ // Register with the global registry
1127
+ mapRegistry.addCameraListener(handleCameraChange);
1128
+
1129
+ // Try to get initial center
1130
+ const mapRef = mapRegistry.getMap();
1131
+ if (mapRef?._currentCenter) {
1132
+ handleCameraChange(mapRef._currentCenter);
1133
+ }
1134
+
1135
+ return () => {
1136
+ mapRegistry.removeCameraListener(handleCameraChange);
1137
+ };
1138
+ }, [core]);
1139
+
1140
+ useEffect(() => {
1141
+ core.initialize();
1142
+ return () => {
1143
+ core.destroy();
1144
+ };
1145
+ }, [core]);
1146
+
1147
+ return (
1148
+ <GridRenderLayer
1149
+ ref={gridLayerRef}
1150
+ opacity={renderProps.opacity}
1151
+ dataRange={renderProps.dataRange}
1152
+ belowID="AML_-_terrain"
1153
+ />
1154
+ );
1155
+ });
1156
+
1157
+ WeatherLayerManager.getAvailableVariables = (options) => {
1158
+ if (!options || !options.apiKey) {
1159
+ console.error("API key must be provided to get available variables.");
1160
+ return [];
1161
+ }
1162
+ const core = new AguaceroCore({ apiKey: options.apiKey });
1163
+ return core.getAvailableVariables('mrms');
1208
1164
  };