18 KiB
STEP-052 正式数据源接入下一阶段调查报告
1. Git / 调查基线
- 仓库:
MapLibreParticleLab - 分支:
particle/main - Starting HEAD:
e4ab1b9d421a4559fa3627df07ee91dcd9b680e4 origin/particle/main:e4ab1b9d421a4559fa3627df07ee91dcd9b680e4- STEP-049:
CLOSED / PASS - STEP-051:
CLOSED / PASS - 调查性质:代码与既有文档只读;本任务不修改功能代码。
2. 结论摘要
STEP-051 已把一条真实可验证的 local production-like 链路接到 Core:
asset metadata + binary payload → LocalParticleFieldFeed → ParticleFieldDescriptor → ParticleFieldDataAdapter → ParticleFieldSnapshot → RasterArraySource → Core Timeline/Runtime Field Window → Renderer。
但它仍不是正式生产数据源。当前 local metadata 是新的 maplibre-particle-field/v1,payload 仍复用仓库已有的 canonical particle-fixtures/*/field.f32;RasterArraySource 直接拥有资源请求、candidate 收集、解析和 Timeline 初始化。仓库没有 Particle 专属的 feed coordinator、forecast catalog/manifest、异步 field-arrival API、retry/timeout/stale policy 或专用 transport cache。
MapLibre 通用 FileSource 已提供 asset、filesystem、HTTP、offline database 等资源能力,但这不等于已有 Weather Service。当前最合理的下一实现阶段不是直接绑定天气供应商,而是先建立 provider-neutral feed/manifest 与低频 arrival coordinator,并用 Local/File/Mock transport 验证。之后再单独接入真实 HTTP/provider。
3. STEP-051 后的真实数据链路
3.1 Local feed 起点与终点
入口和 contract:
include/mbgl/particle/field/local_particle_field_feed.hppsrc/mbgl/particle/field/local_particle_field_feed.cpp- metadata:
particle-production/field-00.json至field-03.json - Android metadata:
platform/android/MapLibreAndroidTestApp/src/main/assets/integration/particle-production/
LocalParticleFieldFeed::load(metadataText, payload) 只处理 provider-neutral metadata 和内存 bytes,调用唯一正式 ParticleFieldDataAdapter::decode()。它不打开文件、不请求网络、不保存缓存,也不认识 Android、Timeline 或 Renderer。
STEP-051 的 Android ParticleCoreLayerValidationActivity.kt 使用 production-fields style source,metadata 走 asset://integration/particle-production/field-*.json,binary 仍来自既有 asset://integration/particle-fixtures/*/field.f32。这已经脱离 fixture metadata/wrapper,但 payload 内容尚是验证用 canonical binary,而非远程生产数据。
3.2 Provider-neutral 边界
正式 neutral 边界位于:
ParticleFieldDescriptor + payload bytes → ParticleFieldDataAdapter → immutable ParticleFieldSnapshot。
include/mbgl/particle/field/field_data_adapter.hpp 的 Descriptor/Snapshot 不含 URL、token、HTTP client、provider name、Android object 或 GPU ownership。Descriptor/Snapshot 当前满足 WIND/GLOBAL 的 Runtime 最小输入;Ocean-only field、mask、更多物理变量尚未进入本阶段 contract。
当前仍留在上游/Source 的内容是资源 URL、metadata 文本、文件请求、asset 读取和 candidate 编排。它们不应进入 RasterParticleLayer 或 ParticleFieldSnapshot。
4. Adapter、Snapshot 与 Source 现状
4.1 Adapter / Snapshot
ParticleFieldDataAdapter::decode(const ParticleFieldDescriptor&, const std::vector<char>&, uint64_t)是唯一正式 decode API。ParticleFieldDescriptor已有fieldID、fieldType、displayDomain、generation、validTime、dimensions、bounds、noData、dataVersion、byteLength、stride、layout、encoding、可选 checksum。ParticleFieldSnapshot自有 metadata、velocity 和 valid arrays;不拥有外部 bytes、FileSource、GPU texture 或 Timeline。- generation 必须由 producer/source 在 adapter 前提供,adapter 不把 0 改成 1;
fieldId与validTime由 producer metadata 提供。 OfflineFixtureParticleAdapter仍存在,但只是 fixture-v1 validation wrapper,不是正式 production feed。
4.2 RasterArraySource / submit
实际文件:
include/mbgl/style/sources/raster_array_source.hppsrc/mbgl/style/sources/raster_array_source.cppsrc/mbgl/style/sources/raster_array_source_impl.hppsrc/mbgl/style/conversion/source.cpp
STEP-051 新增 RasterArrayLocalFieldDescriptor、local candidates 和 submitInitialRuntimeFieldWindow(T0, T1)。Source 对每个 metadata/field URL 使用 FileSource::request(Resource::source(...)),收齐后交给 LocalParticleFieldFeed,再由 bridge 建立 authoritative initial T0/T1。后续 Timeline 从 local candidates 构造 entries,使用 Snapshot 的 producer validTime。
已有 updateRuntimeFieldWindow(ParticleFieldSnapshot) 是已有 T0/T1 之后的最短 submit 路径,继续实现 OLD_T1 → NEW_T0、only NEW_T1、Duplicate NOOP 和 fail-closed。它不是一个独立的外部 feed queue,也没有 feed-level arrival state、manifest index 或持久化生命周期。
4.3 Timeline / Playback
RasterArrayTimelineRuntime 位于 raster_array_source.hpp/.cpp,只消费 immutable Snapshot entries 和严格递增 timestamp,持有 Core-owned cursor/speed/paused/gap/invalid 状态;cache capacity 当前为 8。
这属于 Timeline memory cache,不是 transport/download cache,也不是 persistent field cache。Timeline 已足够消费外部 Snapshot,下一阶段不应重写其 state machine 或 Playback ownership。缺口在 Source/feed producer 如何按时间目录、到达、失败和预取 Snapshot。
5. 仓库已有 transport / network / cache 能力
已有但属于 MapLibre 通用基础设施
include/mbgl/storage/file_source.hpp:异步request()、Resource、cache-only 能力、pause/resume 和资源转发。include/mbgl/storage/resource.hpp:asset/file/network资源请求语义、loading method、storage policy、etag/expiry 等 HTTP/cache 元数据。src/mbgl/storage/file_source_manager.cpp:按类型与配置复用 FileSource 实例。platform/android/MapLibreAndroid/src/cpp/asset_manager_file_source.cpp:Androidasset://读取。platform/android/MapLibreAndroid/src/cpp/http_file_source.cpp与include/mbgl/storage/http_file_source.hpp:Android 通用 HTTP FileSource。include/mbgl/storage/database_file_source.hpp:MapLibre ambient cache/offline database 的 put、invalidate、clear、容量和 revalidation API。include/mbgl/util/lru_cache.hpp:通用内存 LRU 工具。include/mbgl/storage/response.hpp:通用成功、NotFound、Server、Connection、RateLimit、etag、expires、retryAfter 等 Response/Error 字段。
这些能力可以作为 STEP-053 的底层 transport 适配点,但目前没有 Particle field 的 manifest、字段语义、时间序列目录、payload checksum policy、retry/stale policy 或 adapter 错误映射。
当前不存在的 Particle 正式能力
搜索当前 src/、include/、Android JNI/Host 和测试区域,没有发现:
- Weather Service client、Open-Meteo/Windy/vendor client;
- Particle provider abstraction、provider registry 或 provider-specific decoder;
- GRIB/NetCDF/external weather decoder;
- Particle remote manifest/forecast endpoint contract;
- Particle 专用 download queue、transport coordinator、persistent field cache;
- Particle retry、timeout、stale-data、gap inventory 或 availability state policy;
- token/auth/config 与 Particle field service 的绑定。
因此不能把 MapLibre 的通用 HTTP/FileSource 误认成 Weather Service 已接入。
6. Cache、preload 与数据生命周期
当前存在四种不同层次,不能合并:
RasterArrayTimelineRuntime的 entries/cache capacity 8:已经解码的 Snapshot memory cache,服务 Timeline cursor。RasterArraySource的 local/runtime candidate vectors:Source-owned preload candidates,服务初始窗口和后续 boundary。- MapLibre
FileSource/DatabaseFileSourceambient/offline cache:通用资源缓存,按 Resource URL/HTTP metadata 工作,不理解 fieldId、validTime、generation 或 Snapshot。 - Renderer GPU slots:RasterParticleLayer/validation layer 的 T0/T1 texture 与 simulation state,不是下载缓存,不能由数据源层直接管理。
STEP-051 当前是四个资源请求全部到达后构建 Timeline;没有独立的 manifest-first inventory、有限并发下载、field dedup、retry 或 stale policy。已有 MapLibre cache 可以降低资源获取成本,但不能替代 Particle feed lifecycle。
建议下一阶段先定义低频 field arrival/availability 状态和 manifest 顺序,明确:manifest 可用但 payload 缺失、metadata reject、payload mismatch、gap、stale、retry exhausted 时如何保持 last valid Field。Persistent cache 不应在没有 feed key/manifest 生命周期之前先做;第一步只需要 memory/injected transport validation,是否接 DatabaseFileSource 应后置。
7. Forecast manifest/catalog 最小模型
不要把所有未来业务字段塞入 Descriptor。进入正式 feed 必要的最小分层如下。
已属于 ParticleFieldDescriptor/Snapshot
- field identity:
fieldID - producer revision:
generation - forecast
validTime fieldType/displayDomain- grid width/height、bounds
- vector layout/encoding/noData
- expected byte length、stride、data version、可选 payload checksum
这些字段是解码、插值兼容性、fail-closed 和 Renderer canonical input 所必需的。
应属于上游 manifest/catalog,而不是 Snapshot/Renderer
- forecast
runTime/cycle leadTime(可由 runTime 与 validTime 推导,但目录可显式记录)- ordered snapshot list / window membership
- grid identity、resolution、coverage version
- metadata URL 与 payload URL/resource key
- source revision、availability、retry hint、expiry/stale policy
manifest 可以有 source/provider identity,但它不应穿过 Adapter 进入 Snapshot,也不应被 Renderer 或 Runtime Window 解释为业务 provider。
目前不应新增
不要仅为“以后可能需要”加入 level、ensemble、unit system、auth token、HTTP headers、vendor schema、download path、cache database key 或 ocean mask 字段。若未来 Wave/Tide/Current 需要这些语义,应单独设计并遵守 Ocean-only 与 mask 边界。
8. Weather Service 直接接入判断
当前不具备安全的直接接入条件。虽然 Android 有 HTTPFileSource,MapLibre 有通用 Response/cache/error,当前 Particle 仍缺少:
- provider-specific metadata/payload decoder 与其到 neutral Descriptor 的映射;
- forecast run/lead/valid-time inventory;
- provider-independent feed API 和 arrival ordering;
- retry/timeout/stale/gap policy;
- service auth/config ownership;
- 可在不改 Renderer/Timeline 的情况下将网络数据稳定送入 Snapshot submit。
直接接入会把 HTTP/provider 选择、目录和失败策略塞进 RasterArraySource 或 Android Host,容易越过冻结边界。结论:先建立 provider-neutral feed/manifest coordinator,再以独立阶段接 Weather Service;真实 network 不是 STEP-052 后的立即实现目标。
9. 候选下一阶段
| 候选 | 目标 | 新架构/模块 | Network/Provider | Timeline/Renderer | Android | 主要风险 |
|---|---|---|---|---|---|---|
| A. 直接 Weather Service | 立即接真实远程天气接口 | 新增 client、decoder、auth、生命周期 | 需要 | 不应修改,但 Source 会膨胀 | 需要一次高成本 build/test | 缺少 manifest、retry、provider 边界,容易把业务逻辑塞入 Core/Host |
| B. Provider-neutral Feed/Manifest Coordinator | 先定义 manifest、低频 arrival、Snapshot submit 和错误生命周期,用 Local/File/Mock transport 验证 | 一个薄跨平台 feed/coordinator;不建立 Provider Framework | 不需要;可注入 Local/Mock transport | 不改 state machine;不改 Renderer | 若只做 Core 可不需要;若补入口则合并一次 | arrival ordering、initial T0/T1 和 last-valid 语义需冻结 |
| C. Transport Cache/Preload | 先建立持久缓存、重试、过期和预取 | 新增 cache policy/lifecycle | 可不需要 | 不改 Renderer,但会依赖未定义 feed key | 建议需要 | 过早把 MapLibre ambient cache 当 Particle cache,生命周期和 key 尚未稳定 |
| D. Provider Decoder | 先做 GRIB/NetCDF 或某一供应商 decoder | 明显 provider-specific 模块 | 通常需要 | 输出应是 neutral Snapshot | 视入口而定 | 当前无真实 provider contract,容易锁死错误 schema |
候选 B 能以最小修改面冻结真正缺口,并兼容未来 Local/File、HTTP 和 provider decoder;C 应在 B 的 manifest/key/error contract 之后做,A/D 应后置。
10. 唯一推荐的下一实现阶段
Recommended Next Implementation: STEP-053 Provider-neutral Field Feed / Manifest 与 Arrival Coordinator
目标范围:
- 定义最小 provider-neutral field manifest/catalog 与 ordered snapshot arrival contract。
- 将 metadata/payload transport 与
ParticleFieldDataAdapter、ParticleFieldSnapshot、现有 initial T0/T1/update submit 接口连接起来。 - 以 injected Local/File/Mock transport 验证 initial T0/T1、subsequent T1、strict validTime ordering、duplicate、gap、invalid、retry exhausted 和 last-valid hold。
- 只做低频异步 callback/future 或等价 arrival API,不做 Android per-frame JNI,不拥有 Timeline cursor/speed/pause。
- 建立结构化 feed availability/error 分类,明确 metadata reject、payload mismatch、not-yet-available、gap 和 stale 的边界。
- 保持 provider/vendor identity、URL、auth、HTTP headers 和 cache policy 在上游 feed/transport 层,不进入 Snapshot、Runtime 或 Renderer。
输入:manifest/catalog entry、metadata bytes、payload bytes,或注入的 transport response。
输出:已通过正式 Adapter 的 ParticleFieldSnapshot/arrival result;初始窗口和后续窗口仍调用现有 Core submit/update 语义。
允许修改:新增薄的 Core-neutral feed/coordinator、Source 的最小接线、定向 C++ tests、local/mock transport test seam 和必要日志/文档。若增加 Android validation 入口,应与同一阶段其他功能合并一次 build/install。
禁止修改:Timeline state machine、Playback、Runtime Field Window 语义、RasterParticleLayer/GPU simulation/trail、WeatherColorLayer、Adapter 合法性、Provider Framework、Weather Service、HTTP client、vendor decoder、第二套 Snapshot/Timeline/Window。
STEP-053 默认不需要真实 network、服务器、真实 provider 或 Android APK;先完成 Linux/Core/OpenGL 精确验证即可。只有要验证 Android Host 的低频 feed arrival/错误可观测性时才需要一次 Android build/install。
11. 建议的后续 2~4 阶段顺序
- STEP-053 Provider-neutral Field Feed / Manifest 与 Arrival Coordinator:先冻结 manifest、arrival、错误和 last-valid 生命周期,使用 Local/File/Mock transport。
- STEP-054 HTTP Transport 与 Weather Service Adapter:在不改变 neutral Snapshot/Runtime/Renderer 的前提下,把真实 HTTP/provider decoder 置于 feed 上游;此阶段才处理 auth/config 和真实服务契约。
- STEP-055 Field Cache、Retry、Stale 与 Preload Hardening:基于已冻结的 manifest key、generation、validTime 和 error policy,决定 memory/cache/database 组合、重试和容量。
这三个阶段足够表达当前真实缺口,不提前规划更多 provider 或 Ocean field 变体。
12. 平台 / Core / Renderer 责任边界
- Transport/platform:负责 FileSource/HTTP/asset 的资源获取、平台凭据配置和底层 request cancellation;不生成 Timeline 状态。
- Feed/coordinator(建议跨平台 Core-owned seam):负责 manifest 顺序、低频 arrival、metadata/payload 配对、调用正式 Adapter、错误分类和 submit 意图;不负责 GPU。
ParticleFieldDataAdapter:只做 descriptor/payload contract validation 与 canonical Snapshot construction。RasterArraySource:保持 Source-owned authoritative T0/T1、Timeline 接线和已有 Window update;不变成 provider client。- Timeline/Playback:Core-owned,消费 Snapshot 时间和 low-frequency feed 结果;不请求网络。
- Renderer:只消费规范化 Snapshot/Window,继续执行 GPU upload/reuse/simulation/trail;不包含 provider/network/vendor/cache policy。
- Android/Kotlin:只提供低频用户意图和可选资源配置;不拥有 Timeline、Window、feed state 或 per-frame JNI。
13. Android 成本、技术债与风险
后续实现应遵守:先 Linux/Core/OpenGL precise build 与定向测试,再由用户在 Windows 11 对 192.168.1.146 用一次 APK 完成综合验证;若使用 ADB,必须显式 adb -s 192.168.1.146 ...。不为单个 MIX 文本问题单独触发 Android 周期。
Validation UI 的 auto MIX 文本滞后、WAITING/INVALID 视觉提示不足继续是 NON-BLOCKING observability,不阻塞正式 feed 架构,不推荐作为 STEP-053 主任务。Rotation/Viewport 自动恢复继续保留技术债;STEP-053 若不触及 lifecycle/surface/source reload,不合并修复。
主要风险是把 manifest/catalog 与 Descriptor 重复建模,或让 Source 直接拥有下载/重试/缓存策略。应优先薄化并用测试锁定:Snapshot immutable、validTime strict ordering、invalid/gap fail-closed、last valid Field、Window reuse、MIX property-only 和 Renderer 分离。
14. 调查结论
当前可以进入 STEP-053 实现 MD 编写,但不能跳过 provider-neutral feed/manifest 与 arrival 生命周期直接实现 Weather Service。STEP-051 production-like local feed 作为已验证过渡层保留;STEP-052 本身不修改代码、不引入网络、不新增缓存或 Provider。