docs(pbf): archive Full production provenance and engineering history

This commit is contained in:
OpenAI Codex
2026-10-13 14:02:56 +08:00
parent 1fef4c6dd1
commit 07d16c38a8
381 changed files with 107826 additions and 527 deletions

View File

@@ -0,0 +1,438 @@
# Codex 指令:把 NavSea `pickup` 的“当前点击解读”迁移到 React + MapLibre
最后更新:`2026-04-11`
项目根:`/root/sourceserver/pbf`
## 1. 这份文档的用途
这是一份给 `Codex` 的中文任务指令。
目标不是讨论方案,而是让 `Codex` 在目标前端项目里,直接把当前 NavSea 测试页上的 `pickup / 当前点击解读` 逻辑迁移到 `React + MapLibre`。
这份指令要求实现结果尽量与当前测试页一致,而不是重新发明一套新逻辑。
## 2. 你要实现的目标
请在目标前端项目中实现一套 NavSea 海图点击能力,点击后返回并展示“当前点击解读”。
这套逻辑必须满足:
1. 如果点击命中海面上或露出水面的可见对象:
- 直接返回这个对象是什么
- 例如:
- 灯塔
- 灯标
- 浮标
- 建筑物
- 地标
- 防波堤
- 栈桥
- 陆地
- 桥梁
- 危险物
2. 如果点击更适合按海底信息解释:
- 返回当前水深
- 返回物体名(有的话)
- 返回一句简单、普通钓鱼人能看懂的水底情况说明
- 返回“判断理由”
3. 不要让用户看见一堆内部调试术语。
4. 不要默认优先海底解释。
- 只要用户明确点到了看得见的物体,就优先按物体解释。
## 3. 当前实现的唯一参考来源
迁移时,请以本仓库下面这些文件为准,不要自己脑补一套新规则:
- 测试页主逻辑:
- [src/pbf/navsea-pickup-test-karatsu-20nm.html](/root/sourceserver/pbf/src/pbf/navsea-pickup-test-karatsu-20nm.html)
- pickup 解释器:
- [src/pbf/navsea-pickup-interpreter.ts](/root/sourceserver/pbf/src/pbf/navsea-pickup-interpreter.ts)
- [src/pbf/navsea-pickup-interpreter.js](/root/sourceserver/pbf/src/pbf/navsea-pickup-interpreter.js)
- pickup 规则字典:
- [src/pbf/navsea-pickup-rules.v1.json](/root/sourceserver/pbf/src/pbf/navsea-pickup-rules.v1.json)
- 当前前端实现说明:
- [NavSea_Native_Pickup_ReactNative_MapLibre_实现说明.md](/root/sourceserver/pbf/NavSea_Native_Pickup_ReactNative_MapLibre_实现说明.md)
当前测试页里真正要迁移的核心行为,重点看:
- `handlePickup`
- `buildDepthContext`
- `collectTerrainSamples`
- `buildTerrainInterpretation`
- `buildFriendlyClickInterpretation`
- `shouldPreferBBoxPrimary`
## 4. 当前业务口径
迁移时请严格保持下面这些口径。
### 4.1 主键口径
- 主对象 id 使用:
- `feature.id`
- 当前前端可把它视为:
- `fid`
### 4.2 图层口径
- 不允许前端手写一套新的 layer 名单
- 统一从:
- `navsea-pickup-rules.v1.json`
读取:
- `render_layer_groups.primary`
- `render_layer_groups.area`
- `render_layer_groups.info`
- `render_layer_groups.ignore`
### 4.3 点击优先级口径
- 用户点到可见对象时:
- 优先按对象解释
- 用户没点到明确对象时:
- 再回退到海底信息解释
### 4.4 “附近”口径
- 当前业务定义:
- `附近 = 100 米内`
- 不能再直接用固定 `px` 当业务口径
- 正确做法:
1. 先把 `100m` 换成当前 zoom 下对应的屏幕半径
2. 用这个半径做 `queryRenderedFeatures`
3. 再用真实 `distance_m` 二次过滤
4. 只有 `distance_m <= 100` 的对象,才算“附近”
### 4.5 用户文案口径
- 要用普通用户能看懂的话
- 不要把下面这些词直接当最终用户文案:
- 坡折
- 脊
- 槽
- bathymetry
- contour evidence
- 可以保留在内部调试结构里
- 但正式展示应尽量写成:
- `这里水底起伏比较明显,像一道坎。`
- `这里像水底一条沟的边上。`
- `这里像水底一个小包或小凸起边上。`
- `这里水底看起来比较平。`
- `这附近像礁边,鱼容易沿边活动。`
- `当前位置只有少量海底辅助线证据,暂不判断明显坎沟。`
## 5. 需要迁移的功能拆分
请把实现拆成下面几层,不要把所有逻辑塞进一个组件里。
### A. `pickup rules loader`
负责:
- 加载 `navsea-pickup-rules.v1.json`
- 对外提供:
- `primary / area / info / ignore`
- `display_name_overrides`
- `layer_rules`
### B. `rendered feature interpreter`
优先复用或移植:
- [src/pbf/navsea-pickup-interpreter.ts](/root/sourceserver/pbf/src/pbf/navsea-pickup-interpreter.ts)
负责:
- 把 `queryRenderedFeatures` 返回的 feature 转成统一解释结构
- 对每个 feature 输出:
- `fid`
- `source_layer_std`
- `object_domain`
- `object_class`
- `canonical_object_type`
- `title`
- `details`
- `render`
- `score`
- `distance_m`
### C. `pickup query adapter`
负责把 React + MapLibre 的 map 实例封装成统一查询接口。
至少提供:
1. `queryExactFeatures(point)`
2. `queryBBoxFeatures(point, radiusPx)`
3. `queryRawFeatures(point, radiusPx)`
要求:
- `exact` 查询层:
- `area + primary + info`
- `bbox` 查询层:
- `primary + info`
### D. `depth context service`
负责:
- 在对象未命中时,返回最近深度上下文
必须保持与测试页同样口径:
- 当前采用:
- `100 米`
口径
- 返回:
- `nearest_depth_m`
- `nearest_label`
- `nearest_render_layer`
- `nearest_distance_m`
- `candidate_count`
- `candidates`
### E. `terrain interpretation service`
负责:
- 采样 `100 米` 内的海底证据
- 做规则判读
当前只看下面这些强相关层:
- 深度/海底线:
- `depth-contours`
- `depth-contours-major`
- `depth-contour-labels`
- `depth-contours-major-labels`
- `bathymetry-support`
- `bathymetry-depth-labels`
- 底质:
- `bottom-material-labels`
- 结构:
- `submerged-structures`
- `fishery-areas`
- `hazard-polygons`
- `navigation-hazard-polygons`
返回:
- `local_depth_range_m`
- `nearest_depth_m`
- `contour_density`
- `seabed_material`
- `nearby_structures`
- `hints`
### F. `friendly click interpretation service`
这是最重要的一层。
负责把内部 pickup / depth / terrain 结果,变成前端真正展示的“当前点击解读”。
输出必须分两种模式:
1. `海面/露出物`
2. `海底信息`
并返回:
- `mode`
- `headline`
- `depth_text`
- `object_name`
- `shape_text`
- `reason_text`
- `extra_text`
## 6. 必须保留的关键逻辑
### 6.1 `exact` 与 `bbox` 的双阶段查询
不要只做一个 `queryRenderedFeatures`。
必须保留:
1. `exact point query`
2. `bbox query`
理由:
- 海图小符号太小
- 单点容易漏
- bbox 用来补小灯标/小浮标/小地标
### 6.2 `nearby primary` 抢回规则
当前逻辑里,如果:
- `exact` 命中的是 broad area / 次级对象
- `bbox` 命中了 nearby `primary`
则允许 nearby `primary` 抢回。
但下列层不能被轻易抢走:
- `land_area`
- `onshore_structure_area`
- `onshore_structure_line`
- `bridge_structure`
也就是:
- 陆地、建筑物、桥这种可见对象
- 一旦已经被命中
- 不应再被附近别的点对象夺走
### 6.3 可见建筑/地标点必须纳入主入口
当前规则里,这些层已经属于正式 pickup 主入口,迁移时必须保留:
- `facility-points`
- `landmark-points`
- `landmark-point-fallback`
不能只顾海底,不关注建筑/地标。
### 6.4 海底解释不要黑箱
“当前点击解读”里必须展示:
- `判断理由`
当前要求至少能显示:
- 当前水深取最近可见深度,约多少米
- `100米` 内样本深度范围与跨度
- 附近等深线数量
- 深度标签数量
- 海底辅助线数量
- 附近结构或底质
## 7. React 侧建议数据结构
请实现一个统一返回结构,例如:
```ts
export type NavSeaClickInterpretation = {
resultType: "surface_object" | "seabed_context" | "empty";
primary: null | {
fid: string | null;
title: string;
sourceLayerStd: string;
objectDomain: string;
objectClass: string;
canonicalObjectType: string | null;
distanceM: number | null;
details: Array<{ field: string; label: string; value: unknown }>;
};
currentDisplay: {
mode: "海面/露出物" | "海底信息" | "未命中";
headline: string;
depthText: string;
objectName: string;
shapeText: string;
reasonText: string;
extraText: string;
};
depthContext: unknown;
terrainInterpretation: unknown;
candidates: unknown[];
debug?: {
exactFeatures: unknown[];
bboxFeatures: unknown[];
rawFeatures: unknown[];
};
};
```
## 8. React 组件层要求
请在目标前端项目里落一个正式的点击结果 panel。
至少显示下面这些字段:
- `结果类型`
- `主显示`
- `当前水深`
- `物体名`
- `水底情况`
- `判断理由`
- `补充提示`
要求:
- 样式简洁,不需要复刻测试页调试风格
- 但字段含义必须保留
- JSON 调试块可以放到开发模式或 debug 开关下
## 9. 不要做的事
请不要做下面这些事:
1. 不要另造一套新的对象分类体系
2. 不要手写一套新的 layer 白名单
3. 不要把 `附近` 再改回固定像素半径
4. 不要把所有用户都按“海底信息”解释
5. 不要为了“更聪明”擅自引入复杂插值算法
6. 不要删掉当前 `判断理由`
7. 不要把内部调试术语直接暴露给普通用户
## 10. 你应该产出的内容
请在目标前端项目里至少完成:
1. 一个可复用的 `pickup` service / hook
2. 一个可复用的 `current click interpretation` 解释器
3. 一个前端 panel 组件
4. 把 map 点击事件接到这套逻辑上
5. 必要的类型定义
6. 一份简短说明,写清:
- 主要新增了哪些文件
- 如何接入
- 当前有哪些限制
## 11. 验收标准
实现完成后,至少满足下面这些验收条件:
1. 点到灯塔、灯标、浮标、建筑物、地标、防波堤、栈桥、桥梁时:
- 优先返回具体物体
2. 点到空白水域时:
- 不直接返回空
- 返回海底信息
3. “附近”口径稳定为:
- `100 米内`
4. “当前水深”和“判断理由”使用同一套证据口径
5. 海底解释文案对普通钓鱼人基本可理解
6. 不会出现:
- 明明点到建筑物,却解释成海底
7. 不会出现:
- 当前水深和判断理由互相打架
## 12. 建议给 Codex 的直接执行口令
如果你把这份文档直接发给另一个 Codex,可以在前面加上这段:
```md
请在当前 React + MapLibre 项目中,严格按照这份文档实现 NavSea 的 pickup “当前点击解读”。
要求:
1. 先阅读文档中列出的参考文件。
2. 不要另造规则,尽量复用现有 pickup 规则字典和解释器结构。
3. 直接在项目里完成实现,不要只给方案。
4. 完成后给出:
- 改了哪些文件
- 如何接入
- 还剩哪些限制
```
## 13. 当前一句话总结
迁移时请记住唯一重点:
- **可见对象优先按对象解释**
- **海底信息只作为未命中明确对象时的回退解释**
- **“附近”统一按 100 米**
- **“当前点击解读”必须带判断理由**