docs: close STEP-063 release validation
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
383
docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md
Normal file
383
docs/30_设计/MapLibre粒子ReactNative使用说明_v1.md
Normal file
@@ -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<number | null>(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
|
||||
<Map mapStyle={createParticleWeatherStyle()} onDidFinishLoadingStyle={onStyleReady}>
|
||||
<Camera initialViewState={{ center: [135, 32], zoom: 3.9 }} />
|
||||
</Map>
|
||||
```
|
||||
|
||||
`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:<version>`;
|
||||
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”的完整消费链。
|
||||
109
docs/50_Step记录/STEP-063_MapLibre粒子Android_v1发布工程化_v2.md
Normal file
109
docs/50_Step记录/STEP-063_MapLibre粒子Android_v1发布工程化_v2.md
Normal file
@@ -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。
|
||||
91
docs/报告/STEP-063_MapLibre粒子Android_v1发布工程化_Codex完成报告_v2.md
Normal file
91
docs/报告/STEP-063_MapLibre粒子Android_v1发布工程化_Codex完成报告_v2.md
Normal file
@@ -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 文件。
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user