feat: separate S-52 mariner runtime presentation

This commit is contained in:
OpenAI Codex
2026-09-18 06:21:54 +08:00
parent f238f1c471
commit a7c9812bd8
12 changed files with 939 additions and 6 deletions

View File

@@ -0,0 +1,228 @@
# CM95 S-52 NavSea 运行时呈现契约 v1
## 1. 目标
本契约定义 NavSea 如何消费 CM95 Delivery PBF 中的 S-52 海图事实,并在用户修改水深、安全等深线或颜色设置后立即重新呈现,而不重新生成、下载或替换 PBF。
本契约只规定数据与算法责任边界,不在 CM95 Server 中实现 NavSea UI,也不把 NavSea Store/业务生命周期引入 PBF Builder。
## 2. 总体责任
### CM95/PBF 负责
- 几何;
- 原始水深事实的规范化数值;
- Lookup identity 与静态 S-52 Presentation 提示;
- 与用户设置无关的属性派生;
- runtime CSP 所需的最小事实;
- 不可从单个 Feature 自身恢复的必要空间上下文摘要。
### NavSea Runtime 负责
- 当前用户的水深颜色设置;
- `shallowContour / safetyContour / safetyDepth / deepContour`;
- `twoShades`;
- safety contour 实际选择;
- SOUNDG 深/浅字形;
- OBSTRN/WRECKS isolated danger;
- isolated danger 引起的 DisplayBase / priority promotion;
- 设置变化后的即时重算和 MapLibre style 更新。
## 3. Runtime Settings
建议 NavSea 运行时形成一个统一的只读快照,例如:
```text
S52MarinerRuntimeSettings
shallowContourM
safetyContourM
safetyDepthM
deepContourM
twoShades
depthColorRules
```
`depthColorRules` 属于 NavSea 现有的用户自定义显示能力,可以独立于 S-52 四个标准深度阈值。服务器不得把用户颜色写入 PBF。
一次重算必须使用同一个 settings snapshot,避免 DEPARE、DEPCNT、SOUNDG 与 hazard 在同一帧使用不同设置。
## 4. PBF Runtime 字段
### DEPARE / DRGARE
主要字段:
- `depth_min_m`
- `depth_max_m`
- `s52_runtime_csp = DEPARE01 | DEPARE02`
`chart_fill_style` 仅作为当前 NavSea 既有 style 的兼容语义,不应覆盖精确 `depth_min_m/depth_max_m` 的事实地位。
### DEPCNT
主要字段:
- `contour_depth_m`
- `least_depth_m`(兼容)
- `s52_runtime_csp = DEPCNT02`
- `s52_quapos_code`(有值时)
### SOUNDG
主要字段:
- `depth_value_m`
- `s52_sounding_depth_m`
- `s52_sounding_swept`
- `s52_sounding_low_quality`
- `s52_runtime_csp = SOUNDG02`
### OBSTRN / UWTROC / WRECKS
主要字段:
- `s52_hazard_depth_m`
- `s52_surrounding_max_drval1_m`
- `s52_surrounding_min_drval2_m`
- `s52_watlev_code`
- `s52_expsou_code`
- `s52_catobs_code`
- `s52_catwrk_code`
- `s52_quapos_code`
- `s52_quasou_codes`
- `s52_tecsou_codes`
- `s52_status_codes`
- `s52_runtime_csp = OBSTRN04 | WRECKS02`
数组类 code 在 Delivery MVT 中以 JSON 字符串保存,NavSea Adapter 负责一次标准化解析,不由 MapLibre style 直接解析 JSON。
## 5. DEPARE 运行时规则
NavSea 可以有两层规则:
1. 用户自定义 `depthColorRules` 优先决定颜色;
2. 未启用自定义颜色时,可按 S-52 `DEPARE01/02` 的 shallow/safety/deep/twoShades 规则得到标准颜色角色。
颜色计算以 PBF 的精确 `depth_min_m/depth_max_m` 为输入。用户修改阈值只改变已有 DEPARE polygon 的显示,不生成原海图不存在的新等深边界,也不在客户端重新切割 polygon。
因此,例如原始海图区间为 `5–10m`,用户将颜色阈值设为 `7m`,NavSea 可以按规则重新给整个 `5–10m` 区域着色,但不能假装原数据中存在一条精确的 `7m` 面边界。
## 6. DEPCNT 与 safety contour
`contour_depth_m` 是事实;`s52_is_safety_contour` 是运行时结果。
规则语义保持:
- 若存在与 `safetyContourM` 相等的实际等深线,选该深度;
- 否则选择下一条更深的实际等深线;
- 不允许服务器把默认 `3m` 的选择永久写入 runtime PBF。
这里不能只靠单个 Feature 的 MapLibre expression 完成“下一条更深”判断,因为它需要知道可用 contour depth 集合。
NavSea Runtime 因此需要维护稳定的 `ContourCatalog`(按当前有效海图/尺度上下文的已知等深值排序),先计算:
```text
effectiveSafetyContourM = min(depth >= safetyContourM)
```
再把该值注入 DEPCNT style expression。
若当前没有足够的 ContourCatalog 信息,必须 fail-closed:不错误突出某一条线为 safety contour。后续若 Runtime 收集 catalog 的成本过高,可在 PBF 契约增加相邻等深值元数据,但 v1 不先制造新的冗余字段。
## 7. SOUNDG 运行时规则
SOUNDG 的数值深度、扫测状态和质量状态是事实;`SOUNDS* / SOUNDG*` 前缀及最终 symbol sequence 属于运行时结果。
核心规则:
```text
depth <= safetyDepthM -> shallow sounding family
otherwise -> normal sounding family
```
随后根据 swept / low-quality 和数值位数生成最终 S-52 symbol sequence。
用户修改 `safetyDepthM` 后只需重新计算当前可见 SOUNDG 的 presentation,不重新加载 PBF。
## 8. Hazard / isolated danger 运行时规则
OBSTRN/WRECKS 必须根据同一个 `safetyContourM` settings snapshot 重新执行对应 CSP。
PBF 中的 hazard depth、WATLEV、EXPSOU、QUASOU 等是输入事实;以下均为运行时输出,不能直接信任旧 tile 中的固定值:
- isolated danger yes/no;
- `ISODGR51` 等最终符号;
- DisplayBase promotion;
- Hazards priority promotion;
- hazard 内附带 sounding 的 shallow/normal 字形。
当前 PBF 已将相交 DEPARE 的必要深度摘要一起交付,避免 NavSea 为每个 hazard 再做昂贵的 polygon 空间相交。
## 9. 静态 Presentation
以下不依赖水深 mariner settings 的属性仍可直接消费 PBF 物化结果:
- LIGHTS05 静态结果;
- TOPMAR01;
- 静态 TX/TE;
- 静态 SY/LS/LC/AP/AC/CA;
- `s52_symbol_sequence` / `s52_line_sequence`;
- `s52_symbol_rotation_deg`;
- 静态 `OP` 已应用后的 `s52_display_priority`。
NavSea 不应为这些规则重复实现一套与 Server 不同的 Lookup 解释器。
## 10. MapLibre Adapter 边界
建议 NavSea 增加一个纯 Presentation Adapter,而不是把判断分散到各个 UI/Layer:
```text
PBF feature properties
+
S52MarinerRuntimeSettings snapshot
+
ContourCatalog
↓
S52RuntimePresentationAdapter
↓
normalized render properties / style expressions
↓
MapLibre
```
Adapter 不获取网络、不修改用户设置、不拥有 PBF 下载生命周期,只做确定性转换。
## 11. 设置变化
用户修改任何 mariner setting 后:
1. 形成新的 settings snapshot;
2. 更新 effective safety contour;
3. 重新生成/更新 DEPARE、DEPCNT、SOUNDG、hazard 对应 style/runtime properties;
4. 请求 MapLibre repaint;
5. 不清 tile cache,不重新下载 PBF,不重新生成 Server 数据。
禁止继续显示基于旧 settings 推导的 safety contour / isolated danger / sounding shallow 状态。
## 12. Fail-closed
- 缺少精确水深事实时,不凭颜色或旧派生字段反推水深;
- 缺少 ContourCatalog 时,不伪造 safety contour;
- hazard 所需事实不足时,保留普通危险物显示,但不错误声明 isolated danger;
- JSON code 字段解析失败时,视为空集合并记录诊断;
- 运行时设置无效时回退 NavSea 明确定义的默认 settings snapshot,而不是使用 PBF 内历史派生值。
## 13. v1 暂未纳入
`Plain / Symbolized Boundaries` 会改变 Area Lookup Table 选择,尚未完全纳入上述 runtime Adapter。第一版继续沿用当前 boundary table 编译路径;待 NavSea/Style Compiler 能同时持有 Plain 与 Symbolized 两套 Area rule 后再统一运行时切换。
## 14. 接入门槛
NavSea 正式消费 runtime PBF 前至少验证:
- 修改水深颜色不触发 PBF 网络请求;
- 修改 safety depth 后 SOUNDG 即时变化;
- 修改 safety contour 后 DEPCNT 和 hazard 使用同一个新值;
- runtime PBF 中不存在旧固定 `s52_is_safety_contour` / `s52_isolated_danger` 等结果;
- reference PBF 仍可用于 OpenCPN 对照;
- 同一设置、同一 Feature 输入必须得到确定性相同输出。

View File

@@ -0,0 +1,140 @@
# CM95 S-52 静态物化与运行时呈现边界 v1
## 1. 目的
本文件冻结 CM95 → PBF → NavSea 链路中 S-52 Presentation 的责任边界,避免把船员可修改的显示参数提前固化进全国 PBF。
核心原则:PBF 保存海图事实、稳定语义和与船员设置无关的静态 Presentation 提示;NavSea Runtime 负责随用户设置变化的 Presentation 结果。
`S52MarinerSettings` 继续保留,但只作为 OpenCPN/S-52 对照渲染的固定参考配置,不再作为 NavSea Delivery PBF 的唯一真值。
## 2. 两种模式
### 2.1 runtime
`runtime` 是 NavSea Delivery 的默认方向。
对于依赖船员水深设置的 CSP,不在 PBF 构建阶段决定最终颜色、安全等深线、浅深测深字形或 isolated danger 最终状态,而是保留 CSP 名称和运行时所需事实字段。
当前 v1 延迟到 Runtime 的 CSP:
- `DEPARE01`
- `DEPARE02`
- `DEPCNT02`
- `SOUNDG02`
- `OBSTRN04`
- `WRECKS02`
### 2.2 reference
`reference` 用于 OpenCPN/S-52 对照、PoC 和回归验证。
该模式继续使用固定 `S52MarinerSettings`:
- `shallow_contour_m = 2.0`
- `safety_contour_m = 3.0`
- `safety_depth_m = 3.0`
- `deep_contour_m = 6.0`
- `two_shades = false`
只有该模式允许把上述参数推导出的最终 CSP 结果物化到 PBF,用于参考画面对照;这些结果不得被当作 NavSea 用户运行时的永久真值。
## 3. PBF 中必须保留的事实
水深相关事实至少包括:
- `DEPARE/DRGARE`:`depth_min_m`、`depth_max_m`
- `DEPCNT`:`contour_depth_m`,同时兼容现有 `least_depth_m`
- `SOUNDG`:`depth_value_m` / `s52_sounding_depth_m`
- 危险物:`s52_hazard_depth_m`
- 与 isolated danger 判断相关的 `WATLEV`、`EXPSOU`、`CATOBS`、`CATWRK`、`QUAPOS`、`QUASOU`、`TECSOU`、`STATUS` 归一化运行时字段
- 相交水深区摘要:`s52_surrounding_max_drval1_m`、`s52_surrounding_min_drval2_m`
运行时 CSP 通过:
- `s52_mariner_dependent = 1`
- `s52_runtime_csp = <CSP>`
- 未解析的 `s52_csp`
明确标识。
## 4. 不得在 runtime PBF 中固定的结果
以下结果依赖用户/船员设置,不得作为 runtime PBF 的永久真值:
- DEPARE 最终浅水/中水/深水颜色
- `s52_is_safety_contour`
- `s52_sounding_prefix`
- `s52_sounding_is_shallow`
- 由 safety contour 决定的 `s52_isolated_danger`
- 由 isolated danger 引起的 `DisplayBase` promotion
- 由 safety depth/contour 决定的危险物最终符号
`two_shades` 同样属于运行时设置,由 DEPARE Runtime Presentation 处理。
## 5. 仍可静态物化的内容
与船员设置无关、只依赖对象属性和固定 S-52 规则的内容继续在服务器物化,例如:
- LIGHTS05 的灯色、sector 几何、灯质文本和静态符号提示
- TOPMAR01 在已确定 floating/rigid 上下文后的符号
- QUAPOS01、SLCONS03、RESTRN01 等与船员水深阈值无关的 CSP
- 静态 `TX/TE`
- 静态 `SY/LS/LC/AP/AC/CA`
- Lookup 自身的 Display Category / Display Priority,除非 CSP 在运行时明确覆盖
## 6. 对四个待修问题的重新分类
### 6.1 DisplayBase promotion
`UDWHAZ03` 导致的 DisplayBase promotion 属于 mariner-dependent runtime presentation。
因此 runtime PBF 不应把当前固定 safety contour 推导出的 `s52_display_base_promoted=1` 写成永久结果。reference 模式仍可保留该行为用于对照。
### 6.2 OP priority override
`OP(...)` 本身属于 Presentation Rule IR。
- 静态 Lookup/静态 CSP 中的 `OP`:服务器可物化并正确覆盖 `s52_display_priority`
- mariner-dependent CSP 中的 `OP`:必须随 Runtime CSP 一起延迟执行
### 6.3 多个 SY/LS
多个 `SY`、多个 `LS` 属于静态规则序列保真问题,与船员水深设置无关,下一阶段应完整保留顺序,不能只取第一条。
### 6.4 SY ORIENT
通用 `SY(symbol, ORIENT)` 的旋转角属于对象/规则事实,不属于船员水深设置;下一阶段应加入 PBF Presentation Contract,并保留属性引用解析后的角度。
## 7. Symbolized Boundaries
`Plain / Symbolized Boundaries` 本质上也是 mariner presentation 设置,但它会影响 Area Lookup Table 本身,不只是单个 CSP 的输出。
因此 v1 不把它伪装成已完全 runtime 化:
- 当前 `runtime` 模式先解决水深阈值、测深、危险物等深度相关动态结果;
- `s52_boundary_table` 暂时仍用于参考/编译路径;
- 后续由 S-52 Style Compiler / NavSea Runtime 同时持有 Plain 与 Symbolized 规则后,再彻底从 PBF 构建参数中移除该选择。
## 8. CLI 约定
`build-tile` 与 `build-sample-aoi` 增加:
```text
--s52-mariner-mode runtime|reference
```
默认值为 `runtime`。
OpenCPN 对照或需要固定参考画面的任务必须显式使用 `reference`,避免参考参数误进入 NavSea 正式 PBF。
## 9. 本轮后续修复状态
边界冻结后,本轮已经继续完成四项 Presentation 物化修复:
1. 静态 `OP(...)` 已能按 S-52 priority code 覆盖 `s52_display_priority`,并保留 `s52_priority_override`;
2. 多 `LS` 已新增 `s52_line_sequence` / `s52_line_count`,多 `SY` 保留 `s52_symbol_sequence` 并新增 `s52_symbol_count`;
3. `SY(symbol, ORIENT)` 已解析对象属性角度,新增 `s52_symbol_rotation_deg`、`s52_symbol_rotation_source` 与 `s52_symbol_rule_sequence`;
4. reference 模式中的 isolated danger 已将 Display Category 提升为 `Displaybase`、Display Priority 提升为 `Hazards`;runtime 模式只保留事实与 CSP,不固化该结果。
后续应进入单 tile PBF 合同回归和 NavSea Runtime 消费接口设计,而不是重新把上述结果烧回全国 PBF。

View File

@@ -0,0 +1,128 @@
# CM95 S-52 静态运行时边界与四项物化修复阶段报告 v1
日期:2026-09-17
## 本阶段目标
本阶段先解决 NavSea 用户可自定义水深显示与 CM95 固定 S-52 参考参数之间的架构冲突,再继续修复此前发现的四项 PBF Presentation 物化问题。
## 已完成的架构调整
新增 `runtime` / `reference` 两种 S-52 mariner mode。
`runtime` 为 NavSea Delivery 默认方向。以下依赖船员水深设置的 CSP 不再按固定 `S52MarinerSettings` 提前物化最终结果:
- `DEPARE01 / DEPARE02`
- `DEPCNT02`
- `SOUNDG02`
- `OBSTRN04`
- `WRECKS02`
PBF 改为保留运行时所需事实,包括:
- `depth_min_m / depth_max_m`
- `contour_depth_m`
- `s52_sounding_depth_m`
- `s52_hazard_depth_m`
- hazard 周围水深摘要和相关 S-57 code
- `s52_mariner_dependent`
- `s52_runtime_csp`
- 未解析的 `s52_csp`
`reference` 继续按固定 `S52MarinerSettings` 解析,专用于 OpenCPN/S-52 对照。
本次 Delivery 字段与语义发生变化,因此 Builder Contract 已提升为 `cm95pbf-builder/0.4.8-s52-mariner-runtime-boundary`;后续正式 Production Plan 必须重新生成,旧 Plan 不应复用于本版本全国构建。
## 四项物化问题修复
### 1. DisplayBase / Priority side effect
reference 模式下,isolated danger 触发后现在会把:
- `s52_display_category` 更新为 `Displaybase`
- `s52_display_priority` 更新为 `Hazards`
runtime 模式不会写入该动态提升结果。
### 2. OP priority override
`OP(6---)` 等 priority override 现在按 S-52 priority code 映射到最终 `s52_display_priority`,同时保留原始 `s52_priority_override` 供审计。
### 3. 多 SY / LS 保真
- 多 `SY`:保留 `s52_symbol_sequence`,新增 `s52_symbol_count`
- 多 `LS`:新增 `s52_line_sequence` 与 `s52_line_count`
第一条兼容字段继续保留,避免破坏当前消费者。
### 4. SY ORIENT
通用 `SY(symbol,ORIENT)` 现在根据对象属性解析角度,并输出:
- `s52_symbol_rotation_deg`
- `s52_symbol_rotation_source`
- `s52_symbol_rule_sequence`
无法解析角度时 fail-closed,不制造默认方向。
## 验证结果
定向 Python 验证已通过:
- `compileall`:PASS
- runtime DEPARE/DEPCNT/SOUNDG/WRECKS 不再固化 mariner-dependent 最终结果:PASS
- reference WRECKS isolated danger → `Displaybase / Hazards`:PASS
- runtime WRECKS 不产生 `s52_display_base_promoted`:PASS
- `OP(6---)` → `Area Symbol`:PASS
- 双 `LS` sequence 保留:PASS
- `SY(...,ORIENT)` 123.5° 解析:PASS
- Delivery allowlist 对新增深度事实和 structured symbol rule scalarize:PASS
## 已知边界
`Plain / Symbolized Boundaries` 本身也是 mariner presentation 设置,但它涉及 Area Lookup Table 选择,不只是 CSP 输出。当前 v1 没有伪装成已完全 runtime 化;后续需由 S-52 Style Compiler / NavSea Runtime 持有两套边界规则后再解除构建时选择。
## 福冈湾单 Tile PBF 合同回归
已对 `z12/3531/1641` 分别生成 Delivery runtime/reference PBF。两种模式均:
- 输出 Feature:3164
- 输出 Layer:22
- `hard_gate_unmapped`:0
runtime 模式中共有 2260 个 `s52_mariner_dependent=1` Feature:
- `SOUNDG02`:1881
- `DEPCNT02`:188
- `DEPARE01`:172
- `OBSTRN04`:19
这些 Feature 中 `s52_area_color`、`s52_is_safety_contour`、`s52_sounding_prefix`、`s52_sounding_is_shallow`、`s52_isolated_danger`、`s52_display_base_promoted` 均为 0 个,确认固定 mariner 结果没有泄漏进 runtime PBF。
同时实际进入 runtime Delivery PBF 的事实字段包括:
- `depth_min_m`:172
- `depth_max_m`:168(其余对象源数据无有效 DRVAL2)
- `contour_depth_m`:188
- `s52_sounding_depth_m`:1881
- `s52_hazard_depth_m`:19
- 周围水深摘要字段:19
reference 模式中:
- runtime marker:0
- `s52_is_safety_contour=1`:91
- 多 SOUNDG `s52_symbol_count`:1684 个 Feature
- ORIENT rotation 字段:60 个 Feature
本 tile 没有触发 isolated danger / OP override / 多 LS,因此这三项仍由定向规则验证覆盖;reference isolated danger、`OP(6---)` 和双 `LS` 的定向验证均已 PASS。
已新增可重复执行的合同审计工具 `tools/cm95_s52_runtime_contract_check.py`。对本次 runtime/reference 两张 PBF 执行结果为 `PASS`,`dynamic_final_leaks={}`。
## NavSea Runtime 消费契约
已新增 `docs/CM95_S52_NavSea运行时呈现契约_v1.md`,冻结 DEPARE、DEPCNT、SOUNDG、hazard 的 Runtime Adapter 输入/输出、设置快照、ContourCatalog、fail-closed 和无需重载 PBF 的更新流程。
## 下一步
CM95/PBF 侧本阶段可以冻结。下一阶段应在 NavSea 当前实际代码基线上调查现有水深颜色设置、MapLibre style 更新入口和图层 Adapter,再按该契约做最小接入;在 NavSea Runtime 接口验证前,不做全国 PBF 重建。