记忆:专题显示区一组圆(存储/查询/裁剪纪律)+ 前端单测挂进 verify.sh
This commit is contained in:
@@ -1133,3 +1133,78 @@ HTML,一行里必然带 `</script>`,一拒就把正常用法挡死了(我
|
||||
|
||||
|
||||
|
||||
|
||||
## 四十六、专题显示区:一组圆 + 地图点选(2026-10-12)
|
||||
|
||||
**语义变了**:专题的「区域」从**一个圆**变成**一组圆**(并集——落在任一圆内即算在区域内)。
|
||||
起因是用户提的两点:不该只有一个圆;应该在**地图上点选**而不是手填经纬度和半径。
|
||||
(原管理页那句「不需要单独圈选」是文档与实现不一致,见四十五。)
|
||||
|
||||
### 存储:`topics.areas JSONB`,不单开表
|
||||
|
||||
`areas` 是 `[{lat, lon, radiusKm}, ...]`,坐标 **GCJ-02**(地点过滤是拿它跟
|
||||
`places.lat/lon` 直接比球面距离的)。
|
||||
|
||||
为什么用 jsonb 而不是一张 `topic_areas` 表:这些圆是专题定义的组成部分、与专题一起
|
||||
读写、**没有独立生命周期**;单开表还要过一遍「能不能从备份重导」的归属口径、改
|
||||
`dataset_ownership_test.go` 的表白名单、注意迁移块必须排在建表之后 —— jsonb 一次全绕开。
|
||||
|
||||
- 存量回填在 schema.sql 里:`UPDATE topics SET areas = jsonb_build_array(...) WHERE radius_km > 0 AND areas = '[]'`。
|
||||
- ⚠️ `center_lat/center_lon/radius_km` **已废弃但保留**,写入时镜像第 1 个圆:回滚旧二进制时
|
||||
退化成「第一个圆」总比「没有区域」强。判据是三列的注释,别当成还有用的列。
|
||||
- ⚠️ **出库值必须是 `[]` 不是 `null`**:列上有 NOT NULL,而 `json.Marshal(nil slice)` 恰好给
|
||||
`"null"`。用 `topicAreasColumn()` 收敛这一处。
|
||||
- `Topic.AreaList()` 做归一:`Areas` 为空时退回老的三列 ⇒ 未回填的老行与**测试里直接构造
|
||||
Topic 的写法**都继续工作(`topic_stats_test.go` 就是这么写的)。`HasArea()`/`AreasJSON()`
|
||||
都走它,保证「HasArea 为真」与「脚本拿得到圆」永远是同一份判据。
|
||||
|
||||
### 查询:N 个圆的 OR
|
||||
|
||||
`topicAreaCond` 每个圆占 **3 个占位符**(纬度、经度、半径),条件用 ` AND (... OR ...)`。
|
||||
⚠️ 占位符纪律照旧(写错就是「专题内容全变 0」那种难查的事故)。判据写成**定义性不变量**:
|
||||
本段引用的 `$n` 必须**恰好**是 `[prefix+1, len(args)]` 这个连续区间,且最大值 == `len(args)`
|
||||
(列表页那条 UNION ALL 是靠 `len(args)` 自己算编号的,错位就靠这个抓)。注意断言的是
|
||||
「本段自己那 3n 个」,前缀那几个由查询别处引用,**不该**出现在区域条件里。
|
||||
|
||||
### 贴合:`fitCircle` 已删除,统一 `fitBBox`
|
||||
|
||||
多圆之后「圆心 + 半径」表达不了,留着 `fitCircle` 就是两套并行的贴合口径。`fitParams`
|
||||
改为返回所有圆的并集外接矩形(`topicAreasBounds`,高纬处夹一下余弦)——
|
||||
单圆时外接矩形正是那个圆的外接正方形,与原路径等价。
|
||||
|
||||
### 前台:`clip-path: path()`(circle() 只表达得了一个圆)
|
||||
|
||||
- 每个圆一段圆弧子路径、**同绕向(sweep=0)** ⇒ nonzero 填充规则下多段自然取并集,
|
||||
重叠处**不会**互相挖洞。绕向写反的话重叠区会被抵消成一个洞。
|
||||
- 拼路径抽成独立文件 `static/js/fog-clip.js`(`rond-map.js` 是个大 IIFE,函数取不出来,
|
||||
抽出来才能跑 node 单测);⚠️ layout 里**必须排在 rond-map.js 之前**加载。
|
||||
- 给迷雾单独 pane 再裁它(不能裁 `tilePane`,底图在里面);注册时机仍要在 `applyFit()`
|
||||
之后(视图未定时 `latLngToContainerPoint()` 会抛错并把后续图层添加一起打断,见四十五)。
|
||||
- `topic.html` 的 `fogAreas` **无条件下发**(空数组 = 不限区域),别包在 `{{if}}` 里 ——
|
||||
条件分支是「有区域忘了新增分支」这类漏传的温床。
|
||||
- ⚠️ **CSSOM 会把 `clip-path` 规范化**:`style.clipPath` 读回来会被补空格
|
||||
(`path("M 405 296 a 87 87 …")`),而且空格位置不固定(可能出现 `296 a87`)、
|
||||
第一个子路径尾部还可能多一个空格。任何「解析回几何」的代码必须每处分隔都用
|
||||
`\s*`/`\s+` 容忍 —— 写死就等着被咬(本轮连错两版正则)。
|
||||
|
||||
### 前端单测接进 `verify.sh`
|
||||
|
||||
`internal/web/static/js/*.test.js` 以前只靠人记得手动跑 = 等于没有。现在挂在
|
||||
「第 1.5 段」,且**找不到 node 直接失败而不是跳过** —— 跳过会让验收看着全绿、
|
||||
实际一行前端脚本都没验。
|
||||
|
||||
### 验证方法:自校准(别再手算元素坐标)
|
||||
|
||||
`site_cdp.py --clip` 会把裁切框每边外扩 `pad=10` CSS px(×`--scale`),所以
|
||||
「CSS 容器坐标 → 图像像素」不能直接换算。做法是:先拍一张**只画一个圆**的品红探针图,
|
||||
用已知圆心/半径反解出 `scale` 与 `offset`(实测 2.0 / ~19.2),再用它给别处定标。
|
||||
两张图反解出的 scale/offset 必须互相吻合,否则说明取景变了、比较无意义。
|
||||
|
||||
本轮实测(两圆,重叠):A 独有点只有 A 亮、B 独有点只有 B 亮、**重叠中心两圆都亮且
|
||||
并集也亮(无洞)**,整体集合差异 0.013%;迷雾掩码 86,776 px 按并集解析式判**圆外 0 个**,
|
||||
两个圆内分别 52,609 / 46,757 px ⇒ 第二个圆真的被认了。
|
||||
|
||||
### 待办
|
||||
|
||||
后台的**地图点选编辑器**(专题表单内嵌地图、多圆增删拖拽)还没做 —— 这是用户诉求的
|
||||
第二半,专题表单目前仍是三个手填输入框(走兼容桥:合成一个圆写进 areas)。
|
||||
|
||||
Reference in New Issue
Block a user