From 089e7af6eefdf569f210d8374189a06f3aabefe3 Mon Sep 17 00:00:00 2001 From: OpenAI Codex Date: Thu, 17 Sep 2026 11:07:25 +0800 Subject: [PATCH] feat: close S-52 restriction presentation for Fukuoka tile --- docs/报告/CM95_S52_限制区闭合阶段报告_v1.md | 83 +++++++++++++++++++++ src/cm95pbf/presentation.py | 4 +- src/cm95pbf/s52.py | 2 +- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 docs/报告/CM95_S52_限制区闭合阶段报告_v1.md diff --git a/docs/报告/CM95_S52_限制区闭合阶段报告_v1.md b/docs/报告/CM95_S52_限制区闭合阶段报告_v1.md new file mode 100644 index 0000000..51c0372 --- /dev/null +++ b/docs/报告/CM95_S52_限制区闭合阶段报告_v1.md @@ -0,0 +1,83 @@ +# CM95 S-52 限制区闭合阶段报告 v1 + +日期:2026-09-17 +范围:福冈湾参考 tile `z12/3531/1641` + +## 目标 + +完成福冈湾参考 tile 中最后 3 个未解析 CSP: + +- `RESTRN01 × 2` +- `RESARE02 × 1` + +同时补齐 OpenCPN `RESARE02` 会生成的 `OP(...)` Presentation Rule 支持,并确保 Symbolized / Plain boundary 设置进入 S-52 Mariner Settings。 + +## OpenCPN 行为复核 + +两个 FAIRWY: + +- `Central Passage` +- `East Passage` + +均没有 `RESTRN` 属性。按 OpenCPN `RESTRN01` 实际实现,此时不生成额外限制符号,仅视为该 CSP 无追加指令。 + +RESARE 对象属性为 `CATREA=[2]`,无 `RESTRN`。按 `RESARE02` Continuation D,最终选择: + +- `SY(RSRDEF51)` +- Plain boundary 模式:`LS(DASH,2,CHMGD)` + +## 代码调整 + +1. `src/cm95pbf/presentation.py` + - `SUPPORTED_OPCODES` 增加 `OP` + - `static_rule_hints()` 物化 `s52_priority_override` +2. `src/cm95pbf/s52.py` + - `boundary_table == "Symbolized"` 时启用 `symbolized_boundaries` + - Plain boundary 保持普通虚线边界 + +## v9 PBF 验证 + +输出:`build/s52_full_ir_tile_v9/engineering/12/3531/1641.pbf` + +- Feature:3293 +- Layer:23 +- hard-gate unmapped:0 +- unresolved CSP:**0** + +已解析 CSP: + +- SOUNDG02:1881 +- DEPCNT02:188 +- DEPARE01:172 +- SLCONS03:169 +- LIGHTS05:62 +- QUAPOS01:23 +- OBSTRN04:19 +- TOPMAR01:13 +- RESTRN01:2 +- RESARE02:1 + +## 浏览器验收 + +测试页:`presentation_v9.html?hud=collapsed` + +Chrome DevTools Protocol 实际验收结果: + +- 页面标题进入 `READY CM95 福冈湾 S52` +- 非 favicon 浏览器错误:0 +- MapLibre 正常显示既有深度色、测深、灯火、危险物、岸线构造与限制区 +- 截图:`/tmp/cm95_presentation_v9_verified.png` + +## Presentation 全库回归 + +重新执行 OpenCPN `chartsymbols.xml` Presentation Rule IR 审计: + +- Lookup:3057 +- compiled:3057 +- parse failure:0 + +## 结论 + +福冈湾参考 tile 的 S-52 CSP 已达到 **unresolved = 0**。 + +这不等于全日本所有对象/所有 S-52 规则已经完整覆盖。下一阶段应转入全日本 CSP、vector-only symbol、复杂线型和 semantic gap 的系统审计,不再以单 tile 是否清零作为全国完成判断。 diff --git a/src/cm95pbf/presentation.py b/src/cm95pbf/presentation.py index 51222fc..2acd989 100644 --- a/src/cm95pbf/presentation.py +++ b/src/cm95pbf/presentation.py @@ -5,7 +5,7 @@ from typing import Any, Iterable SUPPORTED_OPCODES = frozenset( - {"SY", "LS", "LC", "AC", "AP", "TX", "TE", "CS", "CA", "MP"} + {"SY", "LS", "LC", "AC", "AP", "TX", "TE", "CS", "CA", "MP", "OP"} ) @@ -186,6 +186,8 @@ def static_rule_hints(instruction: str) -> dict[str, Any]: out["s52_arc_end_deg"] = args[5].strip().strip("\'\"") out["s52_arc_radius_mm"] = args[6].strip().strip("\'\"") out["s52_arc_sector_radius_mm"] = args[7].strip().strip("\'\"") + elif rule.opcode == "OP" and args: + out.setdefault("s52_priority_override", args[0].strip().strip("\'\"")) elif rule.opcode in {"TX", "TE"}: out["s52_has_text"] = 1 if rule.opcode == "TX" and args: diff --git a/src/cm95pbf/s52.py b/src/cm95pbf/s52.py index f3568f9..52e371f 100644 --- a/src/cm95pbf/s52.py +++ b/src/cm95pbf/s52.py @@ -299,7 +299,7 @@ class S52LookupRegistry: self.path = Path(path) self.point_table = str(point_table) self.boundary_table = str(boundary_table) - self.settings = S52MarinerSettings() + self.settings = S52MarinerSettings(symbolized_boundaries=self.boundary_table == "Symbolized") rows = json.loads(self.path.read_text(encoding="utf-8")) self.by_class: dict[str, list[dict[str, Any]]] = {} for row in rows: