diff --git a/docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md b/docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md new file mode 100644 index 000000000..e9cc6208c --- /dev/null +++ b/docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md @@ -0,0 +1,383 @@ +# MapLibre 粒子 React Native 使用说明 v1 + +本文面向 NavSea / React Native 调用侧,说明如何通过正式包 `@navsea/maplibre-particle-rn` 使用 MapLibre Particle Android 能力。本文描述的是当前已验证的 `13.4.1-particle.1` 契约,不是 Weather Provider、下载服务或天气业务层设计。 + +## 1. 使用模型 + +React Native App 只直接依赖正式包: + +```text +@navsea/maplibre-particle-rn@13.4.1-particle.1 +``` + +Android Native 由该包的 Expo/Gradle 接线从正式 Maven 仓库解析: + +```text +cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.1 +``` + +正式 Maven 仓库:`https://pbf.jpcn.ccwu.cc/maven`。 + +包会复用唯一的 `@maplibre/maplibre-react-native@11.3.6` peer dependency,并把其 OpenGL Native SDK 请求替换为 Particle AAR。App 不应再并行引入第二份 MapLibre Native AAR、Maven Local AAR、源码路径 bridge 或 MapLibre Native 源码树。 + +以后升级粒子版本的目标流程是:升级 `@navsea/maplibre-particle-rn` 版本 → npm 安装 → Gradle 取得对应正式 AAR → 重新编译 App。 + +## 2. Expo / React Native 配置 + +`app.json` / app config 中加入正式 plugin: + +```json +{ + "expo": { + "plugins": ["@navsea/maplibre-particle-rn/app.plugin"] + } +} +``` + +React 侧统一从正式包导入,不直接从官方 MLRN 或旧 bridge 导入: + +```ts +import { + Camera, + Map, + WeatherRuntime, + createParticleWeatherStyle, + particleWeatherIds, +} from "@navsea/maplibre-particle-rn"; +``` + +`WeatherColor` 与 `RasterParticle` 当前是 MapLibre Core style layer,不是 React 组件。测试或独立页面可直接使用 `createParticleWeatherStyle()`;NavSea 有自己的正式 style 时,应把同样的 `raster-array` source、`weather-color` layer 和 `raster-particle` layer 合入宿主 style,而不是在 MapView 外再画一层 Canvas/WebView。 + +## 3. Map 生命周期与 WeatherRuntime + +必须在 Map style 已完成加载后 attach Native Runtime,并在页面销毁时 detach: + +```ts +const [reactTag, setReactTag] = useState(null); + +const onStyleReady = useCallback(async (event: unknown) => { + const tag = findNodeHandle((event as { target?: unknown }).target as never); + if (tag == null) return; + await WeatherRuntime.attach(tag); + setReactTag(tag); +}, []); + +useEffect(() => () => { + if (reactTag != null) void WeatherRuntime.detach(reactTag); +}, [reactTag]); +``` + +典型 Map: + +```tsx + + + +``` + +`WeatherRuntime.getState(tag)` 返回 Native/Core 权威状态。至少两帧兼容的合法 Field 建立 initial window 后,Runtime 才进入 `READY`;React 不自行维护第二套 T0/T1/MIX Runtime Window。 + +## 4. 显示数据的总体结构 + +这是本接口最重要的部分。每一个时间片不是图片,也不是粒子坐标列表,而是一张**规则二维风矢量场**。一个 Field 由两部分组成: + +```text +metadataJson ++ +local file:// binary payload +``` + +其中 metadata 描述时间、范围、网格和编码;binary payload 只保存每个网格点的 U/V 风矢量。 + +当前正式 schema: + +```text +maplibre-particle-field/v1 +``` + +固定语义: + +```text +二维规则 grid +origin = north-west +row order = north-to-south +column order = west-to-east +storage = row-major +layout = interleaved-uv +encoding = float32 little-endian +U = eastward-positive,单位 m/s +V = northward-positive,单位 m/s +每 cell = 2 × float32 = 8 bytes +``` + +MapLibre 接收的是这张向量场;WeatherColor 根据 U/V 得到色场,RasterParticle 从同一 U/V Field 采样并推进 GPU 粒子。React 不需要提前生成每个粒子的经纬度或轨迹。 + +## 5. metadataJson 结构 + +当前真实 WeatherV2 T0 metadata 的核心结构如下: + +```json +{ + "schema": "maplibre-particle-field/v1", + "fieldId": "ecmwf-hres-wind-2026080700", + "fieldType": "WIND", + "displayDomain": "GLOBAL", + "generation": 1786060800000, + "validTime": 1786060800000, + "dataVersion": 1786060800000, + "bounds": { "west": 112.5, "south": 0.0, "east": 157.5, "north": 55.77657301866769 }, + "grid": { "width": 513, "height": 769 }, + "vector": { + "layout": "interleaved-uv", + "encoding": "float32-le", + "noData": -9999.0, + "unit": "m/s", + "origin": "north-west", + "rowOrder": "north-to-south", + "columnOrder": "west-to-east", + "uPositive": "east", + "vPositive": "north" + }, + "payload": { "byteLength": 3155976 } +} +``` + +字段说明: + +| 字段 | 含义 / 当前约束 | +| --- | --- | +| `schema` | 必须为 `maplibre-particle-field/v1` | +| `fieldId` | Producer 提供的非空 Field 标识 | +| `fieldType` | 当前实现必须为 `WIND` | +| `displayDomain` | 当前实现必须为 `GLOBAL` | +| `generation` | 非 0 `uint64`;Producer 负责赋值,Core 不猜版本 | +| `validTime` | 非 0 `uint64`;同一序列使用同一时间基准并严格递增;当前 WeatherV2 使用 Unix 毫秒 | +| `dataVersion` | 可选 `uint64`,用于数据版本 / identity;当前真实 fixture 提供 | +| `bounds` | `west < east`、`south < north`,描述规则网格覆盖范围 | +| `grid.width/height` | 大于 0;总 cell 数=`width × height` | +| `vector.layout` | 只支持 `interleaved-uv` | +| `vector.encoding` | 只支持 `float32-le` | +| `vector.noData` | 必须为有限数值;当前 fixture 为 `-9999.0` | +| `payload.byteLength` | 必须严格等于 `width × height × 8` | +| `stride` | 可省略;若提供,当前 interleaved UV 必须为 8 bytes | + +真实 fixture 还带 `unit/origin/rowOrder/columnOrder/uPositive/vPositive`、`payload.sha256`、`source` 等审计字段。当前 Native parser 强制读取的是 schema、identity、时间、bounds、grid、layout、encoding、noData 与 byteLength;额外审计字段不会替代上述 canonical 语义。 + +注意:当前 `payload.sha256` 可用于文件生产/发布审计,但 `LocalParticleFieldFeed` 目前只读取 `payload.byteLength`,不要把 `sha256` 写成 Core 已强制校验的安全门槛。 + +## 6. binary payload 的精确排列 + +二维索引: + +```text +cell = y * width + x +``` + +其中 `x=0` 是最西列,`y=0` 是最北行。payload 按 row-major 连续存储:先完整保存最北一行,再向南逐行保存。 + +每个 cell 占 8 bytes: + +```text +byteOffset = cell * 8 +[byteOffset + 0 .. +3] = U,float32 little-endian +[byteOffset + 4 .. +7] = V,float32 little-endian +``` + +等价的 float 序列是: + +```text +U0, V0, U1, V1, U2, V2, ... +``` + +也就是: + +```text +velocity[2 * cell] = U +velocity[2 * cell + 1] = V +``` + +U>0 表示向东,U<0 表示向西;V>0 表示向北,V<0 表示向南。`noData` 只要出现在某 cell 的 U 或 V 任一分量,该 cell 就标记为无效。NaN/Infinity 不是 noData:当前 Adapter 会直接拒绝整张 Field。 + +真实 WeatherV2 fixture: + +```text +width = 513 +height = 769 +cells = 513 × 769 = 394,497 +bytes = 394,497 × 8 = 3,155,976 +``` + +所以 `payload.byteLength` 必须正好是 `3155976`;少 1 byte、多 1 byte 或 metadata 声明与实际文件不一致都会 fail-closed。 + +一个 2×2 示例: + +```text +北行: NW(U=5,V=1) NE(U=6,V=1) +南行: SW(U=4,V=0) SE(U=5,V=0) +``` + +payload 中的 float 顺序必须是: + +```text +5,1, 6,1, 4,0, 5,0 +``` + +总长度为 `2 × 2 × 8 = 32 bytes`。 + +React/Node 侧若自己生成 canonical payload,可用 `DataView.setFloat32(offset, value, true)`;最后一个参数必须为 `true`,明确写成 little-endian。业务层也可以从服务器取得已经生成好的 `.f32` 文件,不要求 JS 重新编码。 + +## 7. React 侧如何提交一帧 + +当前正式 bridge 不把大块 U/V 数据变成 JS `number[]`。React 侧准备好 metadata JSON 和本地文件 URI 后直接提交: + +```ts +const result = await WeatherRuntime.submitWindField( + reactTag, + JSON.stringify(metadata), + payloadFileUri, // 必须是 local file:// URI +); +``` + +测试 Host 使用 `expo-asset` 把随 App 打包的 `.f32` materialize 到本地 cache: + +```ts +const asset = Asset.fromModule(require("./field.f32")); +await asset.downloadAsync(); +const payloadFileUri = asset.localUri; +``` + +正式 NavSea 不要求数据必须来自 Expo Asset。可以由天气业务层下载、转换或合成 canonical U/V Field,然后保存到 App 可读的本地文件,再把 `file://` URI 交给 `WeatherRuntime`。Native 读取 bytes 后进入唯一的 `RasterArraySource::submitHostField()` / Adapter / Snapshot / Timeline 路径。 + +不要使用以下正式 transport: + +```text +number[] +JSON float array +base64 +hex string +每 cell 一个 {lat,lon,u,v} 对象 +React 每帧传粒子位置 +``` + +这些都会扩大 JS/Native 开销并破坏当前已冻结的 binary Field 边界。 + +## 8. 多时间片与 Timeline + +至少提交两个彼此兼容、时间递增的 Field,Core 才能建立 initial window: + +```text +T0 → ACCEPTED_PENDING_INITIAL_WINDOW +T1 → INITIAL_WINDOW_READY +T2 → ACCEPTED_NEXT_SNAPSHOT +T3 → ACCEPTED_NEXT_SNAPSHOT +``` + +随后: + +```ts +const state = await WeatherRuntime.getState(reactTag); +// 预期 state.state === "READY" +``` + +同一时间序列必须保持相同的 grid 尺寸、bounds、noData、fieldType 和 displayDomain,`validTime` 必须使用同一时间基准并严格递增。当前真实 WeatherV2 验收使用 2026-08-07 00/06/12/18 四帧,每帧相隔 6 小时。 + +React 可以用: + +```ts +await WeatherRuntime.setForecastTime(reactTag, targetTimestampMs); +await WeatherRuntime.setPlaybackState(reactTag, "PLAY"); +await WeatherRuntime.setPlaybackSpeed(reactTag, "NORMAL"); +``` + +但 Timeline cursor、T0/T1 选择、时间插值、Runtime Field Window 和 GPU Field 切换仍由 Core 管理;React 只发低频控制命令,不在 JS 中复制一套播放状态机。 + +## 9. 显示与视觉控制 + +当前公开控制包括: + +```text +setWeatherEnabled +setWeatherColorVisible +setParticleVisible +setTargetFps +setWeatherColorStops +setParticleColor +setTrailColor +setParticleCount +setParticleLineWidth +setParticleLength +setParticleOpacity +setTrailOpacity +setTrailFade +setWeatherColorOpacity +``` + +例如: + +```ts +await WeatherRuntime.setWeatherColorVisible(reactTag, true); +await WeatherRuntime.setParticleVisible(reactTag, true); +await WeatherRuntime.setParticleCount(reactTag, 4096); +await WeatherRuntime.setParticleLineWidth(reactTag, 1.5); +await WeatherRuntime.setParticleOpacity(reactTag, 0.9); +await WeatherRuntime.setTrailOpacity(reactTag, 0.8); +await WeatherRuntime.setTrailFade(reactTag, 0.94); +await WeatherRuntime.setWeatherColorOpacity(reactTag, 0.62); +``` + +这些参数只控制显示,不改变 U/V canonical 数据结构。天气业务数据和渲染视觉参数必须分开管理。 + +## 10. 当前 Native 会拒绝哪些数据 + +当前 Adapter 采用 fail-closed。以下情况会拒绝 Field,而不是尽量猜测: + +- schema 不是 `maplibre-particle-field/v1`; +- `fieldId/fieldType/displayDomain` 缺失或类型错误; +- `generation` 或 `validTime` 为 0; +- width/height 为 0; +- `west >= east` 或 `south >= north`; +- `noData` 不是有限数值; +- layout 不是 `interleaved-uv`; +- encoding 不是 `float32-le`; +- stride 提供但不是 8; +- metadata `byteLength` 不等于 `width × height × 8`; +- 实际文件长度与 expected byte length 不一致; +- payload 中包含 NaN/Infinity; +- 当前 fieldType 不是 `WIND`; +- 当前 displayDomain 不是 `GLOBAL`; +- 时间序列不满足既有 compatibility / monotonicity 规则。 + +因此 React 侧不要通过容错转换掩盖 producer 错误。Producer/天气业务层应在提交前完成 canonical 化并保持 Field identity、时间和二进制长度稳定。 + +## 11. 当前已验证的真实数据基线 + +Package Consumer 已使用 ECMWF HRES WeatherV2 真实数据完成 Android 实机验证:4 帧 `2026080700 / 06 / 12 / 18`,每帧 `513×769`、`3,155,976 bytes`,bounds=`112.5,0.0 → 157.5,55.77657301866769`,transport=`FILE_REF / FLOAT32_LE / INTERLEAVED_UV`。用户确认正式 package Consumer APK 正常。 + +## 12. NavSea 侧责任边界 + +推荐保持以下分工: + +```text +React Native / NavSea 天气业务层 + 负责:取数、Route/ETA、多地点/多时间合成、canonical U/V 生成、文件缓存、提交时机 + ↓ metadataJson + local file:// field +@navsea/maplibre-particle-rn + ↓ +MapLibre Native / Core + 负责:校验、Snapshot、Timeline、时间插值、空间采样、WeatherColor、Particle、GPU 资源与渲染 +``` + +MapLibre Native 不理解 provider、HTTP、WeatherV2、Route、ETA 或业务缓存;React 不接管 GPU 粒子模拟和 Core Timeline。 + +## 13. 版本升级检查 + +每次升级 `@navsea/maplibre-particle-rn` 后,至少确认: + +1. npm 根层只有一份 `@maplibre/maplibre-react-native`,没有嵌套 native module; +2. Gradle `dependencyInsight` 最终解析到对应 `cc.navsea.maplibre:android-sdk-particle-opengl:`; +3. 不出现旧 `step061`、Maven Local 或源码路径; +4. Package Consumer 用真实 T0–T3 Field 达到 Runtime `READY`; +5. WeatherColor 与 Particle 均正常显示,地图交互无异常; +6. Consumer 通过后再升级 NavSea 的包版本。 + +正式参考实现:`integration/react-native/MapLibreParticlePackageConsumerTestHost/`。它的意义不是演示 UI,而是验证“正式 npm package → 正式 Maven AAR → 真实 canonical WeatherV2 → Core/Renderer”的完整消费链。 diff --git a/docs/50_Step记录/STEP-063_MapLibre粒子Android_v1发布工程化_v2.md b/docs/50_Step记录/STEP-063_MapLibre粒子Android_v1发布工程化_v2.md new file mode 100644 index 000000000..10bd0a30d --- /dev/null +++ b/docs/50_Step记录/STEP-063_MapLibre粒子Android_v1发布工程化_v2.md @@ -0,0 +1,109 @@ +# STEP-063 MapLibre 粒子 Android v1 发布工程化 v2 + +状态:`CLOSED / PASS` +日期:2026-09-09 + +## 最终结论 + +STEP-063 已完成 MapLibre Particle Android v1 的正式发布工程化,并完成“正式 npm package → 正式 Maven AAR → React Native Consumer → 真实 WeatherV2 → WeatherColor / Particle”的 Android 实机消费验证。 + +正式版本冻结为: + +```text +MapLibre Native upstream : 13.4.1 +npm package : @navsea/maplibre-particle-rn@13.4.1-particle.1 +Android Maven artifact : cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.1 +Maven repository : https://pbf.jpcn.ccwu.cc/maven +renderer : OpenGL +``` + +STEP-061 的 `13.4.1-step061` 只保留为历史开发制品,不再作为正式 Consumer、package 或 Maven 发布依赖。 + +## 正式制品 + +- 正式 Android AAR:166,304,929 bytes;SHA-256 `63c976c5a73fe8f132f428afecca40b09fa2dbdedaa13721586c472ea7eb279d`。 +- 正式 npm 预发布 tarball:`navsea-maplibre-particle-rn-13.4.1-particle.1.tgz`,9,100 bytes。 +- tarball SHA-1:`247e2db901f6588c9e48bc3156a448b97d203d0d`。 +- tarball SHA-256:`31335791a5a6e553a538f84816322e8cd119f16d7c2b15436ce785aa497da6a8`。 + +## Consumer 依赖模型修正 + +Windows 首次 Consumer APK 编译曾因 MLRN 被安装在: + +```text +@navsea/maplibre-particle-rn/node_modules/@maplibre/maplibre-react-native +``` + +导致 RN codegen/CMake 路径超过 Windows 260 字符限制。正式包随后将 `@maplibre/maplibre-react-native@11.3.6` 改为 peer dependency,并保留 devDependency 仅供包自身开发;修正提交为 `fa431ba6fd1f182b8b77345b50a9d74e225f17d4`。 + +重新安装后实际验证: + +```text +root MLRN = present +nested MLRN = absent +``` + +Gradle `dependencyInsight` 同时确认: + +```text +org.maplibre.gl:android-sdk-opengl:13.4.1 +→ cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.1 +``` + +没有回退到官方 Native AAR、Maven Local 或 `step061`。 + +## 真实 WeatherV2 Package Consumer 验证 + +Consumer Test Host 已由 2×2 smoke fixture 升级为真实 WeatherV2 T0–T3 验收 Host;实现提交:`3ac678a3c8164082452bd77e61989ccc928ed8bd`。 + +真实数据固定为 ECMWF HRES 2026-08-07: + +```text +T0 2026080700 +T1 2026080706 +T2 2026080712 +T3 2026080718 +``` + +每帧: + +```text +grid = 513 × 769 +payload = 3,155,976 bytes +schema = maplibre-particle-field/v1 +encoding = float32-le +layout = interleaved-uv +U = eastward-positive m/s +V = northward-positive m/s +bounds = west 112.5 / south 0.0 / east 157.5 / north 55.77657301866769 +transport = metadataJson + local file:// payload +``` + +Linux 静态验证已通过:TypeScript、根层 peer dependency、禁止旧 bridge / direct MLRN import 审计、Metro Android export;Metro 实际识别并导出 4 个 `.f32` assets。 + +Win11 Consumer 编译环境按 NavSea 基线对齐:Gradle Wrapper 8.14.3、buildTools 36.0.0、minSdk 24、compile/target 36、NDK 27.1.12297006、Kotlin Plugin 2.1.20、顶层声明 AGP 8.5.0、RN Gradle Plugin 请求 AGP 8.12.0、JDK 17、New Architecture/Hermes 开启、edge-to-edge 关闭。 + +用户随后手工完成 `assembleDebug`、APK 安装与实体机运行,并明确确认当前正式 Package Consumer APK 正常。结合本轮真实 WeatherV2 Consumer 的设计,STEP-063 的人工 release validation 门槛已通过。 + +## 最终边界 + +本 Step 没有修改 Particle/Core/JNI/WeatherRuntime 业务算法,没有接入 NavSea,没有加入或执行 Jest。正式 package Consumer 不引用 MapLibreParticleLab source path、旧本地 bridge、Maven Local 或手工复制 AAR。 + +当前 release manifest 可以进入“已验证、等待最终 release freeze/tag”状态,但仍然: + +```text +npmRegistry = null +releaseTag = null +``` + +因此 `CLOSED / PASS` 表示 STEP-063 发布工程化与 Consumer 验收完成,不等于 npm 已上传或 release tag 已创建。 + +## React 使用文档 + +React Native 正式调用、生命周期、提交 API 以及 canonical 显示数据结构详见: + +`docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md` + +其中明确冻结:规则二维 NW-origin row-major Grid、`float32-le / interleaved-uv`、每 cell 8 bytes、U eastward / V northward、metadata 字段、byteLength 公式、file-reference transport、Timeline 与 React/Core 责任边界。 + +下一阶段如需正式创建 tag,应单独执行 Release Freeze;不要在本 Step 文档收尾中隐式创建 tag。 diff --git a/docs/报告/STEP-063_MapLibre粒子Android_v1发布工程化_Codex完成报告_v2.md b/docs/报告/STEP-063_MapLibre粒子Android_v1发布工程化_Codex完成报告_v2.md new file mode 100644 index 000000000..e56ed3d42 --- /dev/null +++ b/docs/报告/STEP-063_MapLibre粒子Android_v1发布工程化_Codex完成报告_v2.md @@ -0,0 +1,91 @@ +# STEP-063 MapLibre 粒子 Android v1 发布工程化 — 完成报告 v2 + +结论:`CLOSED / PASS`。 +日期:2026-09-09。 + +## 1. 最终发布契约 + +- MapLibre Native upstream:`13.4.1`。 +- Core commit:`18545ca10d296704b2cba675b182ff2c8da24588`。 +- renderer:OpenGL。 +- npm package:`@navsea/maplibre-particle-rn@13.4.1-particle.1`。 +- Maven:`cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.1`。 +- Maven repository:`https://pbf.jpcn.ccwu.cc/maven`。 +- release manifest:`release/maplibre-particle/release-manifest.json`。 +- Consumer:`integration/react-native/MapLibreParticlePackageConsumerTestHost/`。 + +正式 package 的职责是提供 React Native facade、WeatherRuntime bridge、Expo/Gradle 接线并复用唯一 MLRN peer dependency;正式 Android Native 来自 Maven AAR,不通过源码路径或 Maven Local 消费。 + +## 2. 正式制品与哈希 + +Android AAR:166,304,929 bytes,SHA-256: + +`63c976c5a73fe8f132f428afecca40b09fa2dbdedaa13721586c472ea7eb279d` + +npm tarball:`navsea-maplibre-particle-rn-13.4.1-particle.1.tgz`,9,100 bytes;SHA-1 `247e2db901f6588c9e48bc3156a448b97d203d0d`;SHA-256 `31335791a5a6e553a538f84816322e8cd119f16d7c2b15436ce785aa497da6a8`。 + +## 3. Consumer 依赖问题与最终修正 + +首次 Windows Consumer APK 编译失败原因不是 Particle AAR,而是正式 npm 包把 MLRN 作为普通 dependency,导致 native module 嵌套在 `@navsea/.../node_modules/@maplibre/...`,RN codegen/CMake 路径超过 Windows 260 字符限制。 + +修正提交:`fa431ba6fd1f182b8b77345b50a9d74e225f17d4`,将 `@maplibre/maplibre-react-native@11.3.6` 调整为 peer dependency,并仅保留 devDependency 支撑包自身开发。 + +修正后实际安装验证:根层 MLRN 存在,NavSea package 内嵌 MLRN 不存在;`npm ls` 只形成一份 MLRN native module。Gradle `dependencyInsight` 明确显示: + +```text +org.maplibre.gl:android-sdk-opengl:13.4.1 +→ cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.1 +``` + +因此正式 Consumer APK 中使用的是正式 Particle AAR,而不是 step061、官方 OpenGL AAR、Maven Local 或 MapLibre Native 源码构建。 + +## 4. 真实 WeatherV2 验收 Host + +为了让 Package Consumer 真正模拟未来 NavSea 升级包后的使用方式,Consumer 从原 2×2 smoke fixture 升级为真实 WeatherV2 T0–T3。实现提交:`3ac678a3c8164082452bd77e61989ccc928ed8bd`,message:`test: validate release package with real WeatherV2`,已 push 到 `origin/particle/main`。 + +Consumer 仍只从 `@navsea/maplibre-particle-rn` 导入 `Map`、`Camera`、`WeatherRuntime`;没有重新引入旧 `weather-runtime-bridge` 或 direct MLRN import。 + +真实数据为 ECMWF HRES 2026-08-07 `00/06/12/18` 四帧。每帧 `513×769`、`3,155,976 bytes`,canonical contract 为 `maplibre-particle-field/v1`、`float32-le`、`interleaved-uv`,U eastward、V northward;React transport 为 `metadataJson + local file:// payload`。 + +Linux 已通过: + +- Consumer TypeScript typecheck; +- package peer/hoist 结构检查; +- 禁止 direct MLRN / 旧 bridge 依赖审计; +- Metro `.f32` asset 配置; +- `npx expo export --platform android`,实际导出四个 3.2MB `.f32`; +- `git diff --check`。 + +没有执行 Jest。 + +## 5. Windows / Android 最终人工验收 + +Win11 Consumer Android 环境按 NavSea 当前编译基线对齐:Gradle 8.14.3、buildTools 36.0.0、minSdk 24、compileSdk/targetSdk 36、NDK 27.1.12297006、Kotlin Plugin 2.1.20、JDK 17、RN 0.85.2、New Architecture 与 Hermes 开启。 + +用户手工完成正式 Package Consumer 的 Android 编译和安装,并在实体设备运行;2026-09-09 用户明确确认:`这个APK确认正常了`。该 APK 对应已经升级为真实 WeatherV2 T0–T3 的 Package Consumer,因此 STEP-063 要求的正式 package / Maven AAR / Runtime / WeatherColor / Particle 消费链人工门槛判定 PASS。 + +本报告不把未单独提供的 PID logcat 全量 hard-gate 扫描写成已执行;PASS 依据正式依赖解析、成功 APK 编译安装、真实 Consumer 运行以及用户最终确认。 + +## 6. 关闭边界 + +- 未修改 Particle GPU、WeatherColor 算法、Timeline、HostField canonical contract、JNI/Core 业务功能。 +- 未接入 NavSea 仓库。 +- 未执行 Jest。 +- npm registry 仍为 `null`。 +- release tag 仍为 `null`。 + +因此 STEP-063 的 `CLOSED / PASS` 只表示发布工程化、正式制品和 Package Consumer Android 验收完成;不等于 npm 已正式上传,也不等于 release tag 已创建。下一步应单独执行 Release Freeze / Tag。 + +## 7. React 使用说明 + +新增: + +`docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md` + +该说明从 React Native 使用角度记录:正式包安装模型、Expo plugin、Map attach/detach、WeatherRuntime API、真实 T0–T3 提交方式、NavSea/Core 边界,以及最重要的 canonical 显示数据结构。 + +数据结构明确为规则二维 NW-origin row-major Grid;payload 是 contiguous `float32-le / interleaved-uv`,每 cell 8 bytes,`cell=y*width+x`,U eastward-positive、V northward-positive;metadata 包含 schema、field identity、generation、validTime、bounds、grid、noData 和 byteLength。文档同时区分当前 Native 强制校验字段与仅用于审计的额外 metadata。 + +## 8. Git 基线 + +本轮最终人工验收前的主线实现 HEAD:`3ac678a3c8164082452bd77e61989ccc928ed8bd`,已与 `origin/particle/main` 一致。本文档收尾只允许精确提交本轮新增/更新文档与 release manifest,不提交历史无关 untracked 文件。 diff --git a/release/maplibre-particle/release-manifest.json b/release/maplibre-particle/release-manifest.json index ce56f83a3..32426b1ac 100644 --- a/release/maplibre-particle/release-manifest.json +++ b/release/maplibre-particle/release-manifest.json @@ -1,6 +1,6 @@ { "release": "13.4.1-particle.1", - "releaseState": "prepared-waiting-manual-release-validation", + "releaseState": "validated-ready-for-release-tag", "upstreamMapLibreNative": "13.4.1", "upstreamCoreCommit": "18545ca10d296704b2cba675b182ff2c8da24588", "extensionLine": "particle", @@ -26,6 +26,15 @@ "encoding": "float32-le", "layout": "interleaved-uv" }, + "validation": { + "androidPackageConsumer": { + "status": "pass", + "validatedAt": "2026-09-09", + "consumer": "integration/react-native/MapLibreParticlePackageConsumerTestHost", + "dataset": "ECMWF HRES 20260807 T0-T3", + "transport": "FILE_REF / FLOAT32_LE / INTERLEAVED_UV" + } + }, "publication": { "npmRegistry": null, "mavenRepository": "https://pbf.jpcn.ccwu.cc/maven"