# 执行票:地图标记 DOM overlay 渲染(billboard 发糊与底板生硬的结构性解决) Label: ready-for-agent Category: frontend-execution ## 来源 维护者在批次 3(lanhu-cockpit-asset-replacement 票 16)第七轮走查后的裁定:**「尝试立票做 DOM」**。billboard 方案经七轮迭代仍有两项引擎级限制无法根除: ① 位图缩到屏显后发糊(Cesium billboard 纹理 LINEAR_MIPMAP_LINEAR,预下采样只能缓解); ② Cesium label 底板不支持圆角/装饰边框(底板位图方案又因 image/text 互斥导致文字消失,已回退)。 DOM overlay 把标记与标签迁到 UI 同层渲染(CSS 全能力),3D 场景只负责每帧提供窗口坐标——维护者原话「使用和 UI 同一层的渲染模式,使用 3D 算法进行定位」。 ## 维护者裁定(2026-10-15 两轮走查后,本票方向定调) 1. **「DOM 还存在很大问题,整体还没有还原 billboard 的功能;但显示效果清晰得多——等功能完全实现后再替换 billboard。」** billboard 链路保留(聚合、特效、Canvas 拾取、强调读数继续依赖它),DOM 与 billboard 同位叠放作两层。 2. **渲染分工**:外层母版**自绘 SVG** 还原水滴(素材位图喂 DOM 在高 DPR 屏必糊——糊的根因是预下采样合成时机,不是素材本身);中间行业/设备图标**继续用素材**(去色重染的原生分辨率产物,向下采样永远锐利)。 ## 最终实现(第四轮收口,2026-10-15) `services/markerOverlay.js`(重写)+ `markerAssets.js`(新增 `renderInnerIconUrl`)+ `mapService.js`(接入): - **外层自绘 SVG**:水滴路径按 512px 白母版实测拟合——帽圆 (188.5,189) r189(逐行验证 <1px)+ 下半收尖三次贝塞尔(左缘 57 点最小二乘,最大残差 4.75 源像素 ≈ 0.34 屏显像素);镂空孔 = 实测圆 r75。状态色 `fill` 直注(token 单源 `toneColors()`);降饱和走 CSS filter(与 billboard 同参数)。拟合探针留档 `research/fit-pin-bezier.mjs`。 - **内层行业图标 = 素材**:`renderInnerIconUrl(icon)` 用与 billboard **同一 `recolorInner`** 去色重染规则产出 **256px 原生分辨率** data URL(缓存复用),嵌进同一 SVG 的 ``,按 `plan.inner.diameterRatio`(80%)× 母版宽、帽心对齐——DOM 里只做向下采样,任何 DPR 都锐利。`renderMarker`(billboard 路径)共用该缓存,行为不变。 - **大小对齐 billboard 口径**(第四轮浏览器走查修正两处):① 白描边 `stroke-width=56` + `paint-order="stroke fill"`——彩色母版全尺寸保留、白色纯外扩 28 源像素,与 billboard `withWhiteOutline`(向外膨胀、母版不蚀)同式(此前 stroke 沿中心线,母版被蚀一圈、DOM 比 billboard 小);② 标签限宽取计划层 `labelMaxWidth`(宽×2.6,content-box),替换硬编码 220px——此前 DOM 标签比 billboard 宽 3 倍,两层并现一长一短。 - **贴地定位**:与 billboard `CLAMP_TO_GROUND` 同口径——`engine.groundHeightAt`(地形瓦片实测高程,取不到回椭球 0)+ `projectPivot` 窗口坐标 − host 窗口原点;尖端/描边余量补偿与 billboard `verticalOrigin:BOTTOM + pixelOffset(0,pad)` 同式。 - **点击触发器**:overlay 宿主挂显式 click 派发器,命中 `data-marker-overlay-*` 节点 → **照搬 `pickDeviceOrPointAt`**(与 Canvas 点选链同一功能函数);标记节点 pointerdown/up 停止冒泡防 attachPointPick 双派发;pointermove 不拦(hover/tooltip 照常);标签 `pointer-events:none` 不遮挡相机。 - **节点池**:计划读数指纹(状态/行业/名称/母版/内层/降饱和/色值)变化才重建(NORMAL→ALARM 换色生效);消失隐藏不销毁;`unmountMap` 已接 `detachOverlay` + `unmountMarkerOverlay`。 - **降级**:内层素材加载失败 = SVG 只剩母版水滴;billboard 双层在底下照常显示(链路未动)。 ## 保持不变(契约面) - 合成计划层 `domain/markerPlan.js`(尺寸/锚点/归属/降饱和逻辑原样复用,overlay 只换渲染媒介); - `data-*` 断言面(`markerMaster/markerInner/...` 全部挂到 DOM 节点上); - 状态词表、拾取身份、面板联动、四模式可见性;billboard 链路(`syncPointMarkersForPlan`)完整保留。 ## 核验记录(首版声称 vs 实况,均已修) 首版(迁移自票 20)声称「已实施+走查通过」不成立:SVG 母版路径右半缺失(渲染成月牙碎片);投影原点减法写反(恒偏 host.left/top);内层硬编码 60% 且换算放大 ≈2.2 倍;节点永不刷新;unmount 未接线;「data-* 供点选派发」无消费者。第二/三轮全部修复,第二轮的「renderMarker 位图喂 DOM」经维护者走查证伪(高 DPR 屏放大发糊),第三轮改为自绘 SVG + 原生分辨率内层。 ## 浏览器走查结论(第四轮,2026-10-15 实测,real 数据源) - **形状**:维护者确认水滴还原 OK;大小按 billboard 口径对齐(描边纯外扩、标签限宽同源)。 - **标记**:实心/镂空双形、状态色、降饱和(未关联设备橙色镂空 + 内层窨井图标)渲染正确;内层素材 256px 原生嵌入清晰。 - **点选联动**:监测点(RQ001 燃气监测点001)与未关联设备(TEST-CNXHS0000001 测试消火栓)均正常——详情卡打开、镜头飞行、引线锚定、面板读数字段正确;`data-marker-overlay-key/kind/code` 身份断言面在位。 - **标签**:圆角底板 + 青色装饰边 + 截断省略(与 billboard 同宽口径)+ hover title 读全。 - **控制台**:零业务报错(唯一 error 为第三方百度统计 hm.js 的 permissions-policy 提示,与本票无关)。 ## 遗留(不阻塞本票收口) - **监测点预警/报警特效**:由[票 45](45-untitled.md) 承接(范围第 4 条),特效当前仍在 billboard 层;DOM 复刻特效是**替换 billboard** 的前置之一。 - **聚合符号(K>1)DOM 化**:替换 billboard 的另一前置,未立票(挂地图 Not yet specified 雾区)。 - **billboard 暂不摘除**:按维护者裁定,DOM 完整复刻(特效 + 聚合)后才替换;现状 DOM 标记与 billboard 同位叠放,billboard 特效动画期间底图短暂露边属过渡可接受。 ## 进度 Status: resolved Type: task Blocked by: Labels: ready-for-agent, wayfinder:task ## Comments ### local — 2026-10-15T02:49:19.248Z 维护者第二轮反馈落地:①内层图标直径 60% → 80%(安全边界核验:圆帽区 ≈0.88 倍宽,80% 外沿距帽缘仍有 15 源像素余量);断言同步更新(合成计划 38/38)。overlay 与 billboard 降级路径同受益(同一合成计划)。 ### local — 2026-10-15(本会话执行记录) 执行票 47:认领后对抗式核验首版「已实施」声称,发现六处确定性缺陷(母版路径残缺/投影原点写反/内层几何错/节点不刷新/unmount 未接线/点击派发无消费者),首版走查结论作废。按维护者走查三条反馈重写 overlay:素材改走 renderMarker 同源合成、贴地定位与 CLAMP_TO_GROUND 同口径、显式 click 触发器照搬 pickDeviceOrPointAt、指纹重建、卸载接线。特效需求确认已有票 45 承接(未重复立票)。浏览器复验按维护者指示推迟到功能完全实现后。票保持 open。按 AGENTS.md 跳过 build/dev 验证,仅做 node --check 与离线断言(38/38 过)。 ### local — 2026-10-15T07:11:12.351Z 第三轮(同日,维护者反馈「用素材整体变糊」后裁定:外层自绘 SVG 还原水滴 + 内层行业图标继续用素材): 根因先说清——糊不是素材的错,是合成时机:renderMarker 预下采样到 ~39 CSS px 是为绕 Cesium mipmap,但 DOM 无 mipmap、有 DPR 问题(2× 屏上 39px 源图铺 78 物理像素必然糊;内层火焰 302 源像素被压进 ~30px 更甚)。 实施: ① 外层母版改自绘 SVG:水滴路径按 512 母版实测拟合——帽圆 (188.5,189) r189(逐行验证 <1px)+ 下半收尖三次贝塞尔(左缘 57 点最小二乘,最大残差 4.75 源像素 ≈0.34 屏显像素),镂空孔实测圆 r75;描边 stroke 28 = billboard OUTLINE_WIDTH 同值;viewBox 外扩 40 同 OUTLINE_PAD 裁剪窗。拟合探针留档 .scratch/cockpit-real-data-wayfinding/research/fit-pin-bezier.mjs(含 fit-pin-path.mjs 首轮圆弧检验:收尖非圆弧,残差 338px 排除 A 命令)。 ② 内层素材:markerAssets 新增 renderInnerIconUrl——与 billboard 同一 recolorInner 去色重染规则,但不下采样(256px 原生分辨率 data URL,缓存复用),DOM 按帽径 80% 向下采样永远锐利;renderMarker 改为共用该缓存(billboard 路径行为不变,合成计划断言 38/38 复跑过)。 ③ 降级形态更新:内层素材失败 = SVG 只剩母版水滴;billboard 双层在底下照常(链路未动)。 维护者已授权浏览器复验但指示先完成功能——复验窗口待维护者点名。票保持 open(下一步:浏览器走查同位/同款/点选 + 特效复刻归票 45 + 聚合 DOM 化)。 ### local — 2026-10-15(第四轮:浏览器走查 + 大小对齐收口) 维护者确认形状 OK、指示走查后收口。浏览器实测(real 数据源,http://127.0.0.1:8000/#/cockpit)发现两处尺寸偏差并修正:① 白描边 stroke 沿路径中心线把彩色母版蚀掉一圈(billboard 的 withWhiteOutline 是纯外膨胀)→ 改 `stroke-width=56` + `paint-order="stroke fill"`,母版全尺寸、白环纯外扩 28 源像素同 billboard;② 标签硬编码 max-width:220px 比 billboard 计划层 labelMaxWidth(宽×2.6)宽 3 倍,两层并现一长一短 → 改取 `plan.labelMaxWidth` + content-box。复验:监测点/未关联设备点选联动(详情卡+镜头+引线)、镂空+降饱和+内层窨井图标、标签截断与 hover title 全通过;控制台零业务报错。票关闭。特效(票 45)与聚合 DOM 化是替换 billboard 的前置,留在遗留。 ### local — 2026-10-15T09:25:24.605Z 追加(维护者要求同视角 A/B 对照):临时开关已接——① URL 参数 `#/cockpit?overlay=0`(hash 前后都认)挂载时初始隐藏 DOM 层 = 只露 billboard(之前版本);② DEV 把手 `__cockpitMap.toggleMarkerOverlay()` / `setMarkerOverlay(bool)` / `getMarkerOverlay()` 不重载即时互换(两层同位叠放,切 display 即对照)。浏览器实测两态截图各一张:overlay=0 时标记为位图版(发糊/原生 label),默认态为自绘 SVG 版(锐利/圆角底板标签)。**这是临时走查工具**:billboard 替换收口后随本票遗留项一并移除(markerOverlay.js 的 overlayVisible 两函数 + mapService.js 的 A/B 块与三个把手键)。改动仍未提交,入库裁剪时连本段一起。 ### local — 2026-10-16T01:52:05.232Z 追加(收口后维护者走查反馈两缺陷,已修复并浏览器取证): ① **「设备点图标比老版本小一圈」**——根因:面板强调(`highlightEntities`)把 billboard.scale 改到 1.55,而 DOM 镜像层读不到该量,强调时 billboard 放大、DOM 不动,两层一大小看起来「小了一圈」。修复:引擎新增 `readPointMarkerScales(viewer)`(`kind|code` → billboard.scale 真值),overlay 每帧镜像该量,`transform-origin` 设在尖端(与 billboard `verticalOrigin:BOTTOM` 缩放行为一致)。 ② **「有关联监测点的设备点没有正常应用 DOM」**——根因:关联设备是**独立 billboard 数据源**(`DEVICE_MARKER_SOURCE`,票 42 结构性隔离),DOM overlay 只渲染了摘要标记计划 `markerPlan.markers`,整个设备图层没进 DOM。修复:引擎新增 `projectDeviceMarkers(viewer)`(每帧投影当前可见设备实体:窗口坐标真值含 pixelOffset 尖端补偿 + scale/alpha 反应量真值,折叠聚合个体 `show=false` 不进 DOM——聚合符号按票面 Out of Scope 留在 billboard);mapService 帧状态把设备行与图层计划行并表(name/status/industry/tone)传入 overlay;设备 DOM 节点走与摘要设备同一套合成计划(实心母版 + 内层行业图标),**无名字标签**(billboard 个体设备本就无 label,两层同口径)。 浏览器取证(real):选中城北排水监测点01(CBWS202610150001 报警)→ 关联设备「测试环网监测设备01(21119740)」出现 `data-marker-overlay-kind="associated-device"` 节点(key `device|CBWS…|21119740`,实心 + drainage 内层 + tone #ff3b52 与监测点同状态色),svg 26.72×32.11 = billboard `plan.draw` 同源同值,与 billboard 设备同位叠放;控制台零业务报错。期间见到的 Vue `__vnode` null 循环报错是并行会话 HMR 风暴的开发期残留(硬重载后消失),与本修复无关。改动仍未提交,随票 47 存量 hunks 一起裁剪入库。 ## Answer DOM overlay 渲染层已交付:外层自绘 SVG 水滴(实测拟合,维护者确认形状)+ 内层素材原生分辨率(去色重染同源)+ 真 DOM 标签(圆角/装饰边/截断/hover 全 CSS 能力);大小按 billboard 口径对齐(描边纯外扩、标签限宽单源);贴地定位同 CLAMP_TO_GROUND;点选走显式触发器照搬 pickDeviceOrPointAt;节点池指纹重建、卸载接线。浏览器走查(real 数据源)四类标记渲染 + 两类点选联动 + 标签截断 + 零业务报错全通过。billboard 链路保留(维护者裁定:特效/聚合 DOM 化后才替换)。票 47 关闭。