11 KiB
Codex 指令:把 NavSea pickup 的“当前点击解读”迁移到 React + MapLibre
最后更新:2026-04-11
项目根:/root/sourceserver/pbf
1. 这份文档的用途
这是一份给 Codex 的中文任务指令。
目标不是讨论方案,而是让 Codex 在目标前端项目里,直接把当前 NavSea 测试页上的 pickup / 当前点击解读 逻辑迁移到 React + MapLibre。
这份指令要求实现结果尽量与当前测试页一致,而不是重新发明一套新逻辑。
2. 你要实现的目标
请在目标前端项目中实现一套 NavSea 海图点击能力,点击后返回并展示“当前点击解读”。
这套逻辑必须满足:
- 如果点击命中海面上或露出水面的可见对象:
- 直接返回这个对象是什么
- 例如:
- 灯塔
- 灯标
- 浮标
- 建筑物
- 地标
- 防波堤
- 栈桥
- 陆地
- 桥梁
- 危险物
- 如果点击更适合按海底信息解释:
- 返回当前水深
- 返回物体名(有的话)
- 返回一句简单、普通钓鱼人能看懂的水底情况说明
- 返回“判断理由”
- 不要让用户看见一堆内部调试术语。
- 不要默认优先海底解释。
- 只要用户明确点到了看得见的物体,就优先按物体解释。
3. 当前实现的唯一参考来源
迁移时,请以本仓库下面这些文件为准,不要自己脑补一套新规则:
- 测试页主逻辑:
- pickup 解释器:
- pickup 规则字典:
- 当前前端实现说明:
当前测试页里真正要迁移的核心行为,重点看:
handlePickupbuildDepthContextcollectTerrainSamplesbuildTerrainInterpretationbuildFriendlyClickInterpretationshouldPreferBBoxPrimary
4. 当前业务口径
迁移时请严格保持下面这些口径。
4.1 主键口径
- 主对象 id 使用:
feature.id
- 当前前端可把它视为:
fid
4.2 图层口径
- 不允许前端手写一套新的 layer 名单
- 统一从:
navsea-pickup-rules.v1.json读取:render_layer_groups.primaryrender_layer_groups.arearender_layer_groups.inforender_layer_groups.ignore
4.3 点击优先级口径
- 用户点到可见对象时:
- 优先按对象解释
- 用户没点到明确对象时:
- 再回退到海底信息解释
4.4 “附近”口径
- 当前业务定义:
附近 = 100 米内
- 不能再直接用固定
px当业务口径 - 正确做法:
- 先把
100m换成当前 zoom 下对应的屏幕半径 - 用这个半径做
queryRenderedFeatures - 再用真实
distance_m二次过滤 - 只有
distance_m <= 100的对象,才算“附近”
- 先把
4.5 用户文案口径
- 要用普通用户能看懂的话
- 不要把下面这些词直接当最终用户文案:
- 坡折
- 脊
- 槽
- bathymetry
- contour evidence
- 可以保留在内部调试结构里
- 但正式展示应尽量写成:
这里水底起伏比较明显,像一道坎。这里像水底一条沟的边上。这里像水底一个小包或小凸起边上。这里水底看起来比较平。这附近像礁边,鱼容易沿边活动。当前位置只有少量海底辅助线证据,暂不判断明显坎沟。
5. 需要迁移的功能拆分
请把实现拆成下面几层,不要把所有逻辑塞进一个组件里。
A. pickup rules loader
负责:
- 加载
navsea-pickup-rules.v1.json - 对外提供:
primary / area / info / ignoredisplay_name_overrideslayer_rules
B. rendered feature interpreter
优先复用或移植:
负责:
- 把
queryRenderedFeatures返回的 feature 转成统一解释结构 - 对每个 feature 输出:
fidsource_layer_stdobject_domainobject_classcanonical_object_typetitledetailsrenderscoredistance_m
C. pickup query adapter
负责把 React + MapLibre 的 map 实例封装成统一查询接口。
至少提供:
queryExactFeatures(point)queryBBoxFeatures(point, radiusPx)queryRawFeatures(point, radiusPx)
要求:
exact查询层:area + primary + info
bbox查询层:primary + info
D. depth context service
负责:
- 在对象未命中时,返回最近深度上下文
必须保持与测试页同样口径:
- 当前采用:
100 米口径
- 返回:
nearest_depth_mnearest_labelnearest_render_layernearest_distance_mcandidate_countcandidates
E. terrain interpretation service
负责:
- 采样
100 米内的海底证据 - 做规则判读
当前只看下面这些强相关层:
- 深度/海底线:
depth-contoursdepth-contours-majordepth-contour-labelsdepth-contours-major-labelsbathymetry-supportbathymetry-depth-labels
- 底质:
bottom-material-labels
- 结构:
submerged-structuresfishery-areashazard-polygonsnavigation-hazard-polygons
返回:
local_depth_range_mnearest_depth_mcontour_densityseabed_materialnearby_structureshints
F. friendly click interpretation service
这是最重要的一层。
负责把内部 pickup / depth / terrain 结果,变成前端真正展示的“当前点击解读”。
输出必须分两种模式:
海面/露出物海底信息
并返回:
modeheadlinedepth_textobject_nameshape_textreason_textextra_text
6. 必须保留的关键逻辑
6.1 exact 与 bbox 的双阶段查询
不要只做一个 queryRenderedFeatures。
必须保留:
exact point querybbox query
理由:
- 海图小符号太小
- 单点容易漏
- bbox 用来补小灯标/小浮标/小地标
6.2 nearby primary 抢回规则
当前逻辑里,如果:
exact命中的是 broad area / 次级对象bbox命中了 nearbyprimary
则允许 nearby primary 抢回。
但下列层不能被轻易抢走:
land_areaonshore_structure_areaonshore_structure_linebridge_structure
也就是:
- 陆地、建筑物、桥这种可见对象
- 一旦已经被命中
- 不应再被附近别的点对象夺走
6.3 可见建筑/地标点必须纳入主入口
当前规则里,这些层已经属于正式 pickup 主入口,迁移时必须保留:
facility-pointslandmark-pointslandmark-point-fallback
不能只顾海底,不关注建筑/地标。
6.4 海底解释不要黑箱
“当前点击解读”里必须展示:
判断理由
当前要求至少能显示:
- 当前水深取最近可见深度,约多少米
100米内样本深度范围与跨度- 附近等深线数量
- 深度标签数量
- 海底辅助线数量
- 附近结构或底质
7. React 侧建议数据结构
请实现一个统一返回结构,例如:
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. 不要做的事
请不要做下面这些事:
- 不要另造一套新的对象分类体系
- 不要手写一套新的 layer 白名单
- 不要把
附近再改回固定像素半径 - 不要把所有用户都按“海底信息”解释
- 不要为了“更聪明”擅自引入复杂插值算法
- 不要删掉当前
判断理由 - 不要把内部调试术语直接暴露给普通用户
10. 你应该产出的内容
请在目标前端项目里至少完成:
- 一个可复用的
pickupservice / hook - 一个可复用的
current click interpretation解释器 - 一个前端 panel 组件
- 把 map 点击事件接到这套逻辑上
- 必要的类型定义
- 一份简短说明,写清:
- 主要新增了哪些文件
- 如何接入
- 当前有哪些限制
11. 验收标准
实现完成后,至少满足下面这些验收条件:
- 点到灯塔、灯标、浮标、建筑物、地标、防波堤、栈桥、桥梁时:
- 优先返回具体物体
- 点到空白水域时:
- 不直接返回空
- 返回海底信息
- “附近”口径稳定为:
100 米内
- “当前水深”和“判断理由”使用同一套证据口径
- 海底解释文案对普通钓鱼人基本可理解
- 不会出现:
- 明明点到建筑物,却解释成海底
- 不会出现:
- 当前水深和判断理由互相打架
12. 建议给 Codex 的直接执行口令
如果你把这份文档直接发给另一个 Codex,可以在前面加上这段:
请在当前 React + MapLibre 项目中,严格按照这份文档实现 NavSea 的 pickup “当前点击解读”。
要求:
1. 先阅读文档中列出的参考文件。
2. 不要另造规则,尽量复用现有 pickup 规则字典和解释器结构。
3. 直接在项目里完成实现,不要只给方案。
4. 完成后给出:
- 改了哪些文件
- 如何接入
- 还剩哪些限制
13. 当前一句话总结
迁移时请记住唯一重点:
- 可见对象优先按对象解释
- 海底信息只作为未命中明确对象时的回退解释
- “附近”统一按 100 米
- “当前点击解读”必须带判断理由