docs(pbf): archive Full production provenance and engineering history
This commit is contained in:
438
Codex_指令_NavSea_Pickup_当前点击解读_迁移到_React_MapLibre.md
Normal file
438
Codex_指令_NavSea_Pickup_当前点击解读_迁移到_React_MapLibre.md
Normal 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 米**
|
||||
- **“当前点击解读”必须带判断理由**
|
||||
Reference in New Issue
Block a user