feat: publish Kyushu multi-light S-52 release
This commit is contained in:
266
docs/报告/CM95_S52_灯弧与多LIGHTS修复阶段报告_v2.md
Normal file
266
docs/报告/CM95_S52_灯弧与多LIGHTS修复阶段报告_v2.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# CM95 S-52 灯弧与多 LIGHTS 修复阶段报告 v2
|
||||||
|
|
||||||
|
日期:2026-09-19
|
||||||
|
|
||||||
|
## 1. 最终结论
|
||||||
|
|
||||||
|
本阶段已完成 LIGHTS05 灯弧缺失问题的两层修复:
|
||||||
|
|
||||||
|
1. Web/MapLibre 侧补齐 S-52 CA screen-space renderer;
|
||||||
|
2. Builder 保留同一物理航标关联的全部 LIGHTS presentation,不再只保留一条“最丰富”的 LIGHTS。
|
||||||
|
|
||||||
|
新版九州 Production Release 已完成全量生产、独立 Source→PBF presentation conservation audit、finalize、release-scoped style 编译、CA runtime capability audit 和真实浏览器 multi-sector 验证。
|
||||||
|
|
||||||
|
Data Release 最终状态:**PASS**。
|
||||||
|
|
||||||
|
Target Web debug 的完整 runtime portrayal capability 仍为 **FAIL**,原因是部分 runtime kind 仍为 PREVIEW_ONLY / NOT_IMPLEMENTED;该结果是正确的严格审计结论,不影响本次 Data Release。
|
||||||
|
## 2. 新版基线
|
||||||
|
|
||||||
|
Builder Contract:
|
||||||
|
|
||||||
|
`cm95pbf-builder/0.4.11-s52-multi-light-presentations`
|
||||||
|
|
||||||
|
关键提交:
|
||||||
|
|
||||||
|
- `8ac774dff12bf2f267a9b926551c960528e16be9` — preserve S-52 multi-light arcs
|
||||||
|
- `06dc10abd9b46d2a69c3a75cc2f8a74478ce632e` — enforce S-52 semantic conservation audits
|
||||||
|
- `068efb18141c0a1117925f3bab66c5e16f7cdd76` — scope S-52 conservation audit to production footprint
|
||||||
|
|
||||||
|
Production Plan ID:
|
||||||
|
|
||||||
|
`1c27f0a66f6bacf4484925a4cc3f24d94d50dfd3a9c608fc34f817421b13e94b`
|
||||||
|
|
||||||
|
Release ID:
|
||||||
|
|
||||||
|
`cm95-d1-62a85bd40d3a-1c27f0a66f6b`
|
||||||
|
## 3. Production 最终结果
|
||||||
|
|
||||||
|
- planned tile jobs:5065
|
||||||
|
- PASS:5065
|
||||||
|
- FAILED:0
|
||||||
|
- PBF files:5064
|
||||||
|
- explicit empty tile:1
|
||||||
|
- reports:5065
|
||||||
|
- planned output features:476121
|
||||||
|
- decoded PBF features:476121
|
||||||
|
- tile footprint contract:PASS
|
||||||
|
- S-52 resource contract:PASS
|
||||||
|
- defined resources:1107
|
||||||
|
- active resources:977
|
||||||
|
- S-52 runtime data contract:PASS
|
||||||
|
- release failures:[]
|
||||||
|
|
||||||
|
`semantic_completeness` 仍为 `PENDING_REVIEW`:
|
||||||
|
|
||||||
|
- pending semantic variants:112
|
||||||
|
- pending semantic feature rows:52828
|
||||||
|
|
||||||
|
该 backlog 与本次 LIGHTS / CA 数据守恒修复是不同维度。
|
||||||
|
## 4. Multi-LIGHTS 数据守恒
|
||||||
|
|
||||||
|
Final release 内部的 tile-occurrence 对账:
|
||||||
|
|
||||||
|
- report multi-light groups:357
|
||||||
|
- PBF multi-light groups:357
|
||||||
|
- report light presentation records:745
|
||||||
|
- PBF light presentation records:745
|
||||||
|
- failure count:0
|
||||||
|
|
||||||
|
这里的 357 / 745 是“跨 tile occurrence”计数,同一个稳定 Feature 可在多个 tile 中出现。
|
||||||
|
|
||||||
|
独立 Source→PBF 审计按稳定 Feature ID 去重后的结果:
|
||||||
|
|
||||||
|
- expected multi-light groups:187
|
||||||
|
- actual multi-light groups:187
|
||||||
|
- expected presentation records:390
|
||||||
|
- actual presentation records:390
|
||||||
|
- missing groups:0
|
||||||
|
- unexpected groups:0
|
||||||
|
- presentation signature mismatch:0
|
||||||
|
- tile occurrence presentation drift:0
|
||||||
|
- tile occurrence scope mismatch:0
|
||||||
|
- Source resolution failures:0
|
||||||
|
- PBF parse failures:0
|
||||||
|
|
||||||
|
Presentation Conservation:**PASS**。
|
||||||
|
|
||||||
|
正式 artifact:
|
||||||
|
|
||||||
|
`build/audits/cm95_s52_kyushu_v3_presentation_conservation.json`
|
||||||
|
|
||||||
|
SHA-256:
|
||||||
|
|
||||||
|
`34faff2314996e2daaceb57e7ad3411c5d8ab5e46fa48a874fdd7d66ae5df317`
|
||||||
|
## 5. 审计范围修正
|
||||||
|
|
||||||
|
第一版独立审计曾返回:
|
||||||
|
|
||||||
|
- Source expected groups:193
|
||||||
|
- PBF actual groups:187
|
||||||
|
- Source expected presentations:403
|
||||||
|
- PBF actual presentations:390
|
||||||
|
- missing:17
|
||||||
|
- unexpected:11
|
||||||
|
|
||||||
|
进一步调查确认这不是新版 PBF 再次丢数据,而是审计 expected scope 定义过宽。
|
||||||
|
|
||||||
|
Production 实际范围不是“AOI bbox 内所有允许 scale 的 Source record”,而是:
|
||||||
|
|
||||||
|
`Production Plan + tile buffer + scale ladder + coverage mask + per-tile selected members`
|
||||||
|
|
||||||
|
两个具体误差来源:
|
||||||
|
|
||||||
|
1. 17 个 missing 全部来自 100k Source。它们虽然落在工程 AOI 内,但在对应 zoom 被 finer-scale coverage 遮掉,不会进入任何实际 tile。
|
||||||
|
2. 11 个 unexpected 是合法 tile-buffer occurrence。对象位于裸 AOI 边缘之外,但进入了计划 tile 的 8px expanded bounds,因此 Production 正确输出。
|
||||||
|
|
||||||
|
审计器已改为逐 tile / 逐 native_scale 重放可见区域:
|
||||||
|
|
||||||
|
- 使用与 plan 相同的 `buffer_tile_pixels`;
|
||||||
|
- finer coverage 先占用,coarser scale 使用 `coverage - occupied`;
|
||||||
|
- relation group 只使用该 tile、该 scale 当时实际 visible 的成员;
|
||||||
|
- base 选择在该 selected 集合内重新执行;
|
||||||
|
- expected tile occurrence set 与 PBF actual occurrence set 做严格等值比较。
|
||||||
|
|
||||||
|
修正后全九州审计 187/390 完全一致。
|
||||||
|
## 6. CA 灯弧 Runtime
|
||||||
|
|
||||||
|
CA renderer 按 OpenCPN / S-52 当前语义实现:
|
||||||
|
|
||||||
|
- arc 半径为 screen mm;
|
||||||
|
- sector leg 半径为 screen mm;
|
||||||
|
- outline 与彩色弧分层绘制;
|
||||||
|
- sector start / end 转为屏幕方向;
|
||||||
|
- sector legs 以 screen-space 虚线绘制;
|
||||||
|
- 随 MapLibre move / rotate / resize 重绘;
|
||||||
|
- 不把 CA 预烘焙成地理 PBF geometry。
|
||||||
|
|
||||||
|
release-scoped style audit 的 runtime coverage:
|
||||||
|
|
||||||
|
- RUNTIME_CA:1276 tile occurrences
|
||||||
|
- RUNTIME_DEPARE:47516
|
||||||
|
- RUNTIME_DEPCNT:46189
|
||||||
|
- RUNTIME_SOUNDG:146728
|
||||||
|
- RUNTIME_OBSTRN:45633
|
||||||
|
- RUNTIME_WRECKS:909
|
||||||
|
|
||||||
|
CA capability audit:
|
||||||
|
|
||||||
|
- required kind:RUNTIME_CA
|
||||||
|
- status:IMPLEMENTED
|
||||||
|
- fidelity:S52_SCREEN_MM
|
||||||
|
- evidence failures:0
|
||||||
|
- overall:**PASS**
|
||||||
|
|
||||||
|
Artifact:
|
||||||
|
|
||||||
|
`build/audits/cm95_s52_kyushu_v3_runtime_ca_capability.json`
|
||||||
|
## 7. 完整 Web Runtime Capability
|
||||||
|
|
||||||
|
严格检查当前 Web compare target 的全部 runtime kinds:
|
||||||
|
|
||||||
|
- RUNTIME_CA:IMPLEMENTED
|
||||||
|
- RUNTIME_DEPARE:PREVIEW_ONLY
|
||||||
|
- RUNTIME_DEPCNT:PREVIEW_ONLY
|
||||||
|
- RUNTIME_SOUNDG:PREVIEW_ONLY
|
||||||
|
- RUNTIME_OBSTRN:NOT_IMPLEMENTED
|
||||||
|
- RUNTIME_WRECKS:NOT_IMPLEMENTED
|
||||||
|
|
||||||
|
因此完整 target runtime capability audit:
|
||||||
|
|
||||||
|
**FAIL — runtime_capability_not_implemented**
|
||||||
|
|
||||||
|
Artifact:
|
||||||
|
|
||||||
|
`build/audits/cm95_s52_kyushu_v3_web_runtime_capability.json`
|
||||||
|
|
||||||
|
这个 FAIL 不应被“修成 PASS”。它的作用就是阻止把当前 Web debug preview 描述为完整 S-52 runtime portrayal。
|
||||||
|
## 8. Release-scoped Style
|
||||||
|
|
||||||
|
生成:
|
||||||
|
|
||||||
|
`build/s52_style_compiler_release_1c27f0a66f6b/style.s52.json`
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
- PBF count:5064
|
||||||
|
- source layer count:28
|
||||||
|
- feature count:476121
|
||||||
|
- compiled layers:142
|
||||||
|
- style bytes:1598139
|
||||||
|
- resource contract:PASS
|
||||||
|
- hard_gate_failures:[]
|
||||||
|
|
||||||
|
完整 sprite 继续使用:
|
||||||
|
|
||||||
|
`build/s52_resources_v2/final/s52-sprite-full`
|
||||||
|
## 9. 双-sector 实际浏览器验证
|
||||||
|
|
||||||
|
目标灯标位置约:
|
||||||
|
|
||||||
|
`130.78248061577605E, 34.09885081506925N`
|
||||||
|
|
||||||
|
真实 headless Chrome + MapLibre 运行验证:
|
||||||
|
|
||||||
|
- left style loaded:true
|
||||||
|
- source loaded:true
|
||||||
|
- 稳定 Feature ID:370384
|
||||||
|
- `s52_light_presentations` 同时包含 record 3077 和 3078
|
||||||
|
|
||||||
|
Record 3077:
|
||||||
|
|
||||||
|
- mode:sector
|
||||||
|
- color:LITYW
|
||||||
|
- arc:66° → 106°
|
||||||
|
- radius:15mm
|
||||||
|
- sector radius:25mm
|
||||||
|
|
||||||
|
Record 3078:
|
||||||
|
|
||||||
|
- mode:sector
|
||||||
|
- color:LITYW
|
||||||
|
- arc:116° → 320°
|
||||||
|
- radius:15mm
|
||||||
|
- sector radius:25mm
|
||||||
|
|
||||||
|
CA runtime flatten 后目标 arc features:2。
|
||||||
|
|
||||||
|
当前视口总 lightArcFeatures:9。
|
||||||
|
|
||||||
|
Canvas non-transparent pixels:5920。
|
||||||
|
|
||||||
|
实际 WebGL + CA overlay 截图已生成:
|
||||||
|
|
||||||
|
- `/tmp/cm95_v3_multi_light.png`
|
||||||
|
- `/tmp/cm95_v3_multi_light_left.png`
|
||||||
|
|
||||||
|
因此原先“同一航标多 LIGHTS 只剩一个 sector”的问题已经在最终 release 中闭环验证。
|
||||||
|
## 10. Web Compare 当前状态
|
||||||
|
|
||||||
|
18888 已切到:
|
||||||
|
|
||||||
|
`cm95-d1-62a85bd40d3a-1c27f0a66f6b`
|
||||||
|
|
||||||
|
Health 当前应返回:
|
||||||
|
|
||||||
|
- release_status:PASS
|
||||||
|
- pbf_file_count:5064
|
||||||
|
- decoded_pbf_features:476121
|
||||||
|
- s52_runtime_contract:PASS
|
||||||
|
- s52_light_presentation_contract:PASS
|
||||||
|
- s52_presentation_conservation_contract:PASS
|
||||||
|
|
||||||
|
页面左侧标识更新为:
|
||||||
|
|
||||||
|
`Production v3 / 0.4.11`
|
||||||
|
|
||||||
|
## 11. 最终判断
|
||||||
|
|
||||||
|
本阶段可以确认:
|
||||||
|
|
||||||
|
1. 原 LIGHTS composite 数据损失根因已修复;
|
||||||
|
2. 所有进入最终 release 的 multi-LIGHTS presentation 在 Source→PBF signature 层面守恒;
|
||||||
|
3. tile occurrence scope 与 Production Plan 完全一致;
|
||||||
|
4. CA 灯弧 runtime renderer 已实现并实际绘制;
|
||||||
|
5. 双-sector 实例已真实浏览器验证;
|
||||||
|
6. Data Release 可以正式使用;
|
||||||
|
7. Web compare 仍不是完整 S-52 runtime target,剩余 runtime capability 必须继续逐项实现和审计。
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="toolbar">
|
<div id="toolbar">
|
||||||
<strong>九州 S-52 PBF 对比</strong>
|
<strong>九州 S-52 PBF 对比</strong>
|
||||||
<span class="meta">左:Production v2 本地 PBF 右:extentfix 原始数据源</span>
|
<span class="meta">左:Production v3 / 0.4.11 本地 PBF 右:extentfix 原始数据源</span>
|
||||||
<label><input id="sync-toggle" type="checkbox" checked />同步视图</label>
|
<label><input id="sync-toggle" type="checkbox" checked />同步视图</label>
|
||||||
<button id="reset-btn" type="button">重置九州</button>
|
<button id="reset-btn" type="button">重置九州</button>
|
||||||
<button id="debug-toggle-btn" type="button">调试</button>
|
<button id="debug-toggle-btn" type="button">调试</button>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
<section class="pane" id="left-pane">
|
<section class="pane" id="left-pane">
|
||||||
<div id="left-map" class="map"></div>
|
<div id="left-map" class="map"></div>
|
||||||
<canvas id="left-ca-overlay" class="ca-overlay" aria-hidden="true"></canvas>
|
<canvas id="left-ca-overlay" class="ca-overlay" aria-hidden="true"></canvas>
|
||||||
<div class="pane-title"><b>左:九州 Production v2</b><span id="left-state" class="state">等待参考 style…</span></div>
|
<div class="pane-title"><b>左:九州 Production v3 / 0.4.11</b><span id="left-state" class="state">等待参考 style…</span></div>
|
||||||
<div id="left-inspector" class="inspector empty">点击任一侧海图,对比同一位置的 Feature。</div>
|
<div id="left-inspector" class="inspector empty">点击任一侧海图,对比同一位置的 Feature。</div>
|
||||||
<div class="divider-label">LOCAL</div>
|
<div class="divider-label">LOCAL</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -478,8 +478,8 @@ function cloneReferenceForLocal(referenceStyle){
|
|||||||
source.minzoom=5;
|
source.minzoom=5;
|
||||||
source.maxzoom=12;
|
source.maxzoom=12;
|
||||||
source.bounds=[128.0,29.5,132.5,34.8];
|
source.bounds=[128.0,29.5,132.5,34.8];
|
||||||
source.attribution="CM95 九州 S-52 Production Pipeline v2";
|
source.attribution="CM95 九州 S-52 Production 0.4.11 multi-light";
|
||||||
style.name=(style.name || "NavSea") + " / Kyushu Production v2";
|
style.name=(style.name || "NavSea") + " / Kyushu Production v3 / 0.4.11";
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,7 +492,7 @@ function prepareS52Style(raw){
|
|||||||
source.minzoom=5;
|
source.minzoom=5;
|
||||||
source.maxzoom=12;
|
source.maxzoom=12;
|
||||||
source.bounds=[128.0,29.5,132.5,34.8];
|
source.bounds=[128.0,29.5,132.5,34.8];
|
||||||
source.attribution="CM95 九州 S-52 Production Pipeline v2";
|
source.attribution="CM95 九州 S-52 Production 0.4.11 multi-light";
|
||||||
style.sprite=location.origin + "/s52/sprite";
|
style.sprite=location.origin + "/s52/sprite";
|
||||||
if(referenceStyleTemplate && referenceStyleTemplate.glyphs){
|
if(referenceStyleTemplate && referenceStyleTemplate.glyphs){
|
||||||
style.glyphs=referenceStyleTemplate.glyphs;
|
style.glyphs=referenceStyleTemplate.glyphs;
|
||||||
@@ -550,9 +550,9 @@ leftMap.on("style.load",function(){
|
|||||||
const sources=Object.keys((leftMap.getStyle() || {}).sources || {});
|
const sources=Object.keys((leftMap.getStyle() || {}).sources || {});
|
||||||
if(sources.includes("cm95")){
|
if(sources.includes("cm95")){
|
||||||
installRuntimePreviewLayers(leftMap);
|
installRuntimePreviewLayers(leftMap);
|
||||||
setState(leftState,"Production v2 / S-52 style" + (debugRuntimePreview.checked ? " + runtime preview" : ""),"ok");
|
setState(leftState,"Production v3 / 0.4.11 / S-52 style" + (debugRuntimePreview.checked ? " + runtime preview" : ""),"ok");
|
||||||
}else if(sources.includes("navsea_delivery")){
|
}else if(sources.includes("navsea_delivery")){
|
||||||
setState(leftState,"Production v2 / extentfix style","ok");
|
setState(leftState,"Production v3 / 0.4.11 / extentfix style","ok");
|
||||||
}
|
}
|
||||||
applyDebugStyles();
|
applyDebugStyles();
|
||||||
refreshLightArcFeatures();
|
refreshLightArcFeatures();
|
||||||
@@ -607,7 +607,7 @@ debugLeftS52.addEventListener("change",function(){
|
|||||||
debugRuntimePreview.addEventListener("change",function(){
|
debugRuntimePreview.addEventListener("change",function(){
|
||||||
if(leftMap.getSource("cm95")){
|
if(leftMap.getSource("cm95")){
|
||||||
installRuntimePreviewLayers(leftMap);
|
installRuntimePreviewLayers(leftMap);
|
||||||
setState(leftState,"Production v2 / S-52 style" + (debugRuntimePreview.checked ? " + runtime preview" : ""),"ok");
|
setState(leftState,"Production v3 / 0.4.11 / S-52 style" + (debugRuntimePreview.checked ? " + runtime preview" : ""),"ok");
|
||||||
applyDebugStyles();
|
applyDebugStyles();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
RELEASE_ID = "cm95-d1-62a85bd40d3a-2b2e7d976a43"
|
RELEASE_ID = "cm95-d1-62a85bd40d3a-1c27f0a66f6b"
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
@@ -28,7 +28,7 @@ def main():
|
|||||||
manifest_path = release_dir / "release_manifest_v1.json"
|
manifest_path = release_dir / "release_manifest_v1.json"
|
||||||
html_path = web_root / "kyushu_compare.html"
|
html_path = web_root / "kyushu_compare.html"
|
||||||
assets_root = project_root / "build" / "fukuoka_s52_web" / "site" / "assets"
|
assets_root = project_root / "build" / "fukuoka_s52_web" / "site" / "assets"
|
||||||
s52_style_path = project_root / "build" / "s52_style_compiler_release_2b2e7d976a43" / "style.s52.json"
|
s52_style_path = project_root / "build" / "s52_style_compiler_release_1c27f0a66f6b" / "style.s52.json"
|
||||||
s52_sprite_root = project_root / "build" / "s52_resources_v2" / "final"
|
s52_sprite_root = project_root / "build" / "s52_resources_v2" / "final"
|
||||||
|
|
||||||
required = [
|
required = [
|
||||||
@@ -73,6 +73,8 @@ def main():
|
|||||||
"pbf_file_count": manifest.get("pbf_file_count"),
|
"pbf_file_count": manifest.get("pbf_file_count"),
|
||||||
"decoded_pbf_features": manifest.get("decoded_pbf_features"),
|
"decoded_pbf_features": manifest.get("decoded_pbf_features"),
|
||||||
"s52_runtime_contract": (manifest.get("s52_runtime_contract") or {}).get("status"),
|
"s52_runtime_contract": (manifest.get("s52_runtime_contract") or {}).get("status"),
|
||||||
|
"s52_light_presentation_contract": (manifest.get("s52_light_presentation_contract") or {}).get("status"),
|
||||||
|
"s52_presentation_conservation_contract": (manifest.get("s52_presentation_conservation_contract") or {}).get("status"),
|
||||||
}
|
}
|
||||||
self.send_bytes(json.dumps(payload, ensure_ascii=False).encode("utf-8"), "application/json; charset=utf-8")
|
self.send_bytes(json.dumps(payload, ensure_ascii=False).encode("utf-8"), "application/json; charset=utf-8")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user