From e15f583c653b2ade894e596207dbda7adfcc3283 Mon Sep 17 00:00:00 2001 From: OpenAI Codex Date: Wed, 9 Sep 2026 20:09:28 +0800 Subject: [PATCH] feat: add WeatherV2 HTTP particle provider --- .../App.tsx | 18 +++ .../README.md | 8 +- .../src/httpWeatherV2Demo.ts | 40 +++++ packages/maplibre-particle-rn/CHANGELOG.md | 3 + packages/maplibre-particle-rn/README.md | 70 ++++++++- packages/maplibre-particle-rn/src/index.ts | 1 + .../src/weatherV2/WeatherV2HttpClient.ts | 138 ++++++++++++++++++ .../src/weatherV2/index.ts | 17 +++ .../src/weatherV2/loadIntoWeatherRuntime.ts | 95 ++++++++++++ .../src/weatherV2/types.ts | 96 ++++++++++++ .../src/weatherV2/wvecbin1.ts | 136 +++++++++++++++++ 11 files changed, 615 insertions(+), 7 deletions(-) create mode 100644 integration/react-native/MapLibreParticlePackageConsumerTestHost/src/httpWeatherV2Demo.ts create mode 100644 packages/maplibre-particle-rn/src/weatherV2/WeatherV2HttpClient.ts create mode 100644 packages/maplibre-particle-rn/src/weatherV2/index.ts create mode 100644 packages/maplibre-particle-rn/src/weatherV2/loadIntoWeatherRuntime.ts create mode 100644 packages/maplibre-particle-rn/src/weatherV2/types.ts create mode 100644 packages/maplibre-particle-rn/src/weatherV2/wvecbin1.ts diff --git a/integration/react-native/MapLibreParticlePackageConsumerTestHost/App.tsx b/integration/react-native/MapLibreParticlePackageConsumerTestHost/App.tsx index a126a7ce0..f65835531 100644 --- a/integration/react-native/MapLibreParticlePackageConsumerTestHost/App.tsx +++ b/integration/react-native/MapLibreParticlePackageConsumerTestHost/App.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { Button, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View, findNodeHandle } from "react-native"; import { Camera, Map, WeatherRuntime, type ColorStop, type RuntimeSnapshot, type TargetFps } from "@navsea/maplibre-particle-rn"; import { materializeRealWeatherFrames, realWeatherFrames } from "./src/realWeatherDataset"; +import { loadHttpWeatherV2 } from "./src/httpWeatherV2Demo"; import { weatherTestStyle } from "./src/weatherStyle"; const initialSnapshot: RuntimeSnapshot = { state: "WAITING_FOR_FIELDS", playbackState: "PAUSE", targetFps: "AUTO", disposed: false }; @@ -18,6 +19,7 @@ export default function App() { const [basemap, setBasemap] = useState("LOADING"); const [frames, setFrames] = useState>({ T0: "PENDING", T1: "PENDING", T2: "PENDING", T3: "PENDING" }); const [transport, setTransport] = useState("PENDING: FILE_REF / FLOAT32_LE / INTERLEAVED_UV"); + const [httpTransport, setHttpTransport] = useState("PENDING: WeatherV2 HTTP → canonical U/V → WeatherRuntime"); const [weatherVisible, setWeatherVisible] = useState(true); const [particleVisible, setParticleVisible] = useState(true); const [visual, setVisual] = useState(initialVisual); @@ -57,6 +59,21 @@ export default function App() { } catch (error) { setTransport(`ERROR: ${String(error)}`); setResult(`LOAD REAL WEATHER T0-T3: ${String(error)}`); } }, [reactTag]); + const loadHttpWeather = useCallback(async () => { + if (reactTag == null) return setResult("MAP_NOT_READY: wait for GSI/style ready"); + try { + setHttpTransport("LOADING: manifest → time index → WVECBIN1 → canonical U/V"); + const loaded = await loadHttpWeatherV2(reactTag); + setSnapshot(loaded.runtime); + setFrames(Object.fromEntries(loaded.frames.map((frame, index) => [`T${index}`, frame.submitResult])) as Record); + setHttpTransport(`READY: ${loaded.frames.length} HTTP frames / ${loaded.context.referenceTime}`); + setResult(`HTTP WeatherV2 READY: ${loaded.frames.map((frame) => frame.fieldId).join(", ")}`); + } catch (error) { + setHttpTransport(`ERROR: ${String(error)}`); + setResult(`LOAD HTTP WEATHER: ${String(error)}`); + } + }, [reactTag]); + const updateVisual = useCallback((key: keyof typeof initialVisual, value: string) => setVisual((current) => ({ ...current, [key]: value })), []); const applyVisual = useCallback(() => void command("particle visual", async (tag) => { const results = await Promise.all([ @@ -76,6 +93,7 @@ export default function App() { Basemap: GSI {basemap}Dataset: ECMWF HRES 20260807 · 513×769 · 3.01 MiB/frameRuntime: {snapshot.state} / {snapshot.playbackState} / {snapshot.targetFps}REAL DATA TRANSPORT = {transport} {realWeatherFrames.map((frame) => {frame.label} {frame.cycle}: {frames[frame.label]})} {result}