tamakyi
|
6aee7e441b
|
给显示区编辑器补上长度参照:比例尺 + 直径标注
用户反馈「专题里的圆无论怎么缩放大小都一样,20km 却覆盖了整个南宁」。实测
三处画圆(编辑器 L.circle、改半径把手、前台迷雾裁剪圆)**全都是地理锚定的**:
编辑器圆直径 z9/z11/z13 = 178/710/2842px(期望 177/711/2847)、同一缩放下改半径
3/10/25/50km → 42/142/356/710px(期望 43/142/355/711)、把手距圆心 20.038km
(该圆半径 20km)、前台裁剪圆换算始终 ≈20km。也就是**复现不出「大小不变」**。
真正的问题是缺**参照物**:编辑器地图以前没有比例尺(前台专题页一直有),
圈圆时只能靠地名猜。用他自己的数据验证「20km 覆盖南宁」是对的——圆心 20km 内
落了他 477 个地点中的 45 个,最远到西乡塘的广西民族大学相思湖校区(14.5km)
与良庆的五象万象汇(10.2km),正是南宁建成区的跨度。
所以这一版补的是参照物,不改任何几何逻辑:
- 编辑器地图加 L.control.scale(跟前台一致,公制)。
- 半径旁边标出跨度:`spanLabel()` 把半径换成「约 40 公里宽」(<1 公里改用米,
免得出「约 0.1 公里宽」这种别扭说法);圆列表每行也标,新圆半径旁边实时跟着变。
- 守卫 TestTopicAreaEditorGivesSizeReference 钉住这两样(变异验证过:去掉比例尺立刻红)。
它顺手抓到我自己的疏漏——模板的 init 调用漏传了 spanId,靠脚本默认值兜着,已补上显式传参。
验证:topic-areas.test.js 55 条(新增 11 条 spanLabel 边界);无头实测比例尺在
(z10 显示 10 km)、新圆半径 3→「约 6 公里宽」/25→「约 50 公里宽」实时更新、
圆列表 20→「约 40 公里宽」,全程无 JS 报错。
|
2026-10-13 10:37:10 +08:00 |
|
tamakyi
|
9c76437a76
|
修:显示区半径输入框被浏览器原生校验误判
现象:输入任意正常半径(3、3.5、7.25…)提交时弹
「请输入有效值。两个最接近的有效值分别为 2.55 和 3.05」。
根因:<input type="number"> 的 **step 是相对 min 递增**的,不是「step 的整数倍」。
min=0.05 + step=0.5 ⇒ 合法值只有 0.05 / 0.55 / 1.05 …,于是 3 和 3.5 全被判非法。
而这是**浏览器原生**提示:JS 与后端都拦不到(我们自己的校验根本没机会说话)。
实测对照(同一元素换 step,其余不动):
step=0.5 :0.05✓ 1✗ 3✗ 3.5✗ 7.25✗ 13.37✗
step=0.05 :0.05✓ 1✓ 3✓ 3.5✓ 7.25✓ 13.37✗ ← 仍会拦任意小数
step=any :全 ✓ ← 采用
保留 min/max(真实边界,且给 spinner 范围),只把 step 放开交给 JS/后端校验,
理由与写法都记在模板注释里。
同一 bug 类加了通配守卫,防的是下次换个页面又踩:
- TestNumberInputStepNeverMismatchesMin:扫**所有**模板的数字框,
凡「有 min 且有数值 step」就要求 min 是 step 的整数倍(或改用 any);
- TestTopicAreaRadiusInputAllowsAnyStep:列表里的输入框是 JS 动态建的,模板扫不到,
单独钉住 step='any'。
变异验证过:把 step 改回 0.5,守卫立刻红。
实测(无头):输入 3.5 后 validationMessage 为空、form.checkValidity() 为真 ⇒
回车提交不再弹气泡;隐藏字段正确更新为 radiusKm:3.5;越界值只给我们的中文提示
(stepMismatch=false、rangeUnderflow=true,说明「step 误判」这一类已消除)。
全站其它数字框(如迷雾遮罩的 radius_m,min=50/step=50)本来就合法,未改动。
|
2026-10-12 18:11:53 +08:00 |
|
tamakyi
|
7ab33227a5
|
专题显示区改成在地图上点选(后台编辑器)
用户诉求的后半:不该让他手填经纬度和半径,应该在地图上圈。三个手填输入框
换成地图 + 圆列表:点地图加圆、拖圆心移动、拖圆边改半径、列表里可改半径/
定位/删除、可一次清空;改动立刻写进隐藏字段 areas(JSON) 与 crs,随专题表单
一起提交。
- 复用 /admin/fog-mask 那套约定:/api/map-config 取底图、底图坐标系随表单带上、
后端换算成 GCJ-02。不另起炉灶。
- areas 的初值**渲染进隐藏字段**(不是只塞给 JS):脚本没加载上时提交的仍是原值,
否则一次保存就把已有显示区静默清空。
- 地图初始视野取生效数据集的地点包围盒(placesBounds),一上来就落在自己的地界上,
而不是全国视野。
- 上限(20 个 / 0.05~2000 km)前端也校验一份,并**禁用提交**;两边数字用守卫比对,
免得出现「前端放行、后端拒绝,用户不知道改哪里」。
实测抓到并修掉的 bug:/admin/topics 没设 p.HasMap ⇒ 布局不引入 Leaflet,
表现是脚本加载了、rondTopicAreas 也在,就是 window.L 永远 undefined,init 静默
返回、控制台不报错、页面像「什么都没发生」。已加守卫钉住这一点。
其余:
- topic-areas.js 只暴露纯函数(校验/序列化/增删改的不可变性/几何换算)供 node 单测,
另加一个最小测试钩子 api._map 供无头验证(同 window.rondMap._state 惯例)。
- CSS 用定宽容器装半径输入框:站点全局 input 规则是 width:100% 且带 10 个 :not()
(特异性 (0,11,1)),直接给 input 写宽度压不过。
- gofmt:文档注释里的缩进列表按 Go 1.19 的规则重排;顺带把工作区的 CRLF 归一到 LF
(.gitattributes 早就声明 eol=lf,blob 本来也是 LF,所以 git diff 无额外噪声)。
验证:
- topic-areas.test.js 44 条(含「增删改返回新数组不原地改」、半径换算自洽性)。
- 三个新守卫:两边上限一致、模板契约(旧输入框必须没了 + 挂点齐全 + 初值来源)、
topicsAdminPage 必须设 HasMap。
- 无头端到端:两击成两圆 → 改半径 7.5 → 删第 1 个 → 越界半径被拦且禁用提交 →
提交 200 且落库 areas=[{lat:24.72,lon:108.1,radiusKm:7.5}](废弃列镜像 7.5);全程无 JS 报错。
|
2026-10-12 17:35:00 +08:00 |
|
tamakyi
|
44763dfc3f
|
专题的多圆显示区:前台按并集裁剪迷雾
上一批把显示区改成了「一组圆」,这一批让前台真正认多个圆。
- clip-path 从 circle() 换成 path():circle() 只表达得了一个圆。每个圆一段
圆弧子路径、**同绕向**(sweep=0),nonzero 填充规则下多段自然取并集,
重叠处不会互相挖洞。
- 拼路径的活单独放 fog-clip.js(rond-map.js 是个大 IIFE,函数取不出来),
这样能直接跑 node 单测;layout 里必须排在 rond-map.js 之前加载。
- 顺带清掉已经死掉的 fitCircle:多圆之后「圆心 + 半径」表达不了,留着就是
两套并行的贴合口径。fitParams 现在只给 fitBBox(单圆时外接矩形正是那个
圆的外接正方形,视野等价)。
- 守卫改名 TestTopicFogIsClippedToAreas,钉住两端(模板发 fogAreas ↔ 脚本认
fogAreas)与两处顺序(裁剪注册在 applyFit 之后、fog-clip.js 在 rond-map.js
之前);fogAreas 必须**无条件**下发(空数组 = 不限区域),不要包在条件里。
- verify.sh 挂进前端单测:以前只靠人记得手动跑 = 等于没有。找不到 node 直接
失败而不是跳过(跳过会让验收看着全绿、实际一行前端脚本都没验)。
验证(临时两圆专题,SQL 造的,已删):
- fog-clip.test.js 15 条:把生成的 path 解析回圆弧再验几何,含绕向一致性与
半径 0/负数的夹取。
- 端到端像素验证:自校准出「CSS 容器坐标 → 图像像素」(scale≈2、offset≈19.2,
正是 site_cdp 的 --clip 外扩 10px×scale,别再手算);三张掩码图证明并集语义
—— A 独有点只有 A 亮、B 独有点只有 B 亮、**重叠中心两圆都亮且并集也亮
(无洞)**,整体集合差异 0.013%。
- 迷雾掩码:86,776 个像素,按两圆并集的解析式判 **圆外 0 个**;两个圆内分别
有 52,609 / 46,757 个迷雾像素 ⇒ 第二个圆真的被认了,不是只看第一个。
|
2026-10-12 16:44:55 +08:00 |
|
tamakyi
|
1bb35dbbc9
|
专题显示区改为一组圆(后端与查询层)
把专题的「一个圆」换成「一组圆」(areas jsonb 数组):落在任一圆内即算在
区域内(SQL 里的 OR)。这一批只动后端与查询层,前台渲染与后台地图编辑器
分别跟进;后台表单暂时沿用原来的三个坐标输入框,保存时合成一个圆写进
areas,所以现有交互不受影响。
- topics.areas JSONB + 存量回填:老的单圆搬进去,之后 areas 是唯一权威。
废弃的 center_lat/center_lon/radius_km 保留(写入时镜像第一个圆),
一是回滚旧二进制时还能退化显示成第一个圆,二是回填的输入。
- Topic.AreaList() 归一:Areas 为空时退回老的三列,未回填的老行与直接构造
Topic 的调用方都继续工作。
- topicAreaCond 改成 N 个圆的 OR;每个圆占 3 个占位符,编号纪律照旧
(写错就是「专题内容全变 0」那种难查的事故)。
- fitParams 改用所有圆的并集外接矩形:多圆之后「圆心+半径」表达不了,
统一走 fitBBox;单圆时外接矩形就是这个圆的外接正方形,与原路径等价。
- 坐标系:编辑器提交的圆心若是 WGS-84 底图上点的,存前换算成 GCJ-02
(地点过滤要拿它跟 places 的 GCJ-02 坐标比距离)。
测试:
- 表单解析(两个入口同一套校验)、wgs84 换算、半径为 0/空 = 不限区域、
坏 JSON/越界必须报错、上限 20 个。
- 占位符纪律:本段引用的 $n 必须恰好是 [prefix+1, len(args)] 连续区间,
最大值必须等于参数个数。
- 多圆并集语义(DB 门控):两圆专题的地点集合必须恰好等于两个单圆专题的
并集 —— 定义性不变量,变异验证过(只认第一个圆 ⇒ 两个用例同时红)。
|
2026-10-12 16:29:46 +08:00 |
|