归属判定升级:DataV 官方行政区划表 + 同名县消歧

用阿里 DataV GeoAtlas(免密钥)替换 OSM Overpass 离线县表:
- 民政部 adcode 体系,县级前 4 位即所属地级码,2832 县 100% 可定位地级
  (OSM 表只有 1182/2362,且 admin_level 层级不可靠)
- 同名县(全国 30 组)保留全部候选,运行时按坐标消歧,
  修掉「柳州的城中区被归到西宁市」

归属判定改为「按名字精确查官方表」优先于「按中心点猜最近的县」:
窄长县按中心点会跨省错判(环江喀斯特实际在环江县,按中心点判到荔波县)
查表带距离校验,精确全称命中放宽到 150km(狭长县中心离边界可达 85km+)

修掉三个真实缺陷:
- Overpass 兜底把地级塞进 osmCounty、县名塞进 osmCity,
  与 attachPlaces「先找县级」的顺序相反,导致兜底路径永远丢下层县名
- Overpass 请求无硬超时,公共实例「连着不返回」能把一次分析拖成十几分钟;
  补三层限(单次超时 / 阶段总时限 / is_in 反查次数上限),13 分钟 → 48 秒
- Photon 的 osm.place.city 类目混着景点 POI,kind==='城市' 时无条件把名字
  当地名,导致「社更穿洞北门」「柳州之门」等景点自己成一组;
  改为必须同时是行政区名且能在官方表查到

新增离线回归测试(都不打外部网络,Photon/Overpass 挂掉也能回归):
- test-admin-lookup:同名县消歧、已知边界点、狭长县远端点(19 项)
- test-overpass-fallback:本地假 Overpass,验兜底路径、简称归一、200+HTML 错误页
- test-grouping-regression:本地假 Photon,20 候选完整分组
- verify-ui-full:端到端 UI 验收(真实无头 Chrome 读真实 DOM)
This commit is contained in:
2026-11-06 02:05:24 +08:00
parent c5c2ffae68
commit 7ad86c6619
25 changed files with 2766 additions and 13 deletions
+8 -2
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="行知 TripSense:按时间与所在地分析周边适合出行的目的地,叠加天气与交通,并由 AI 生成逐日行程规划。">
<title>行知 · 周边出行顾问 | TripSense</title>
<link rel="stylesheet" href="styles.css?v=18">
<link rel="stylesheet" href="styles.css?v=19">
</head>
<body>
<header class="topbar">
@@ -149,13 +149,19 @@
<button data-sort="distance">距离最近</button>
<button data-sort="time">单程最短</button>
</div>
<div class="search-box">
<input type="search" id="candSearch" placeholder="搜索地名或景点名" autocomplete="off" aria-label="搜索候选目的地">
<button type="button" class="search-clear hidden" id="candSearchClear" title="清空">✕</button>
</div>
<label class="check"><input type="checkbox" id="onlyRec"> 仅显示「推荐」及以上</label>
<label class="check"><input type="checkbox" id="onlyNiche"> 只看小众</label>
<label class="check"><input type="checkbox" id="groupByPlace" checked> 按地名分组</label>
<button class="btn-outline sm" id="compareBtn" type="button">对比已选</button>
<span class="selected-count" id="selectedCount">已选 0 个</span>
</div>
<div class="preselect-hint hidden" id="preselectHint">已自动预选评分最高的 3 个目的地,可点击卡片按钮取消或改选</div>
<div class="filter-note hidden" id="filterNote"></div>
<div class="compare-panel hidden" id="comparePanel"></div>
<div class="cards" id="cards"></div>
<div class="notes" id="dataNotes"></div>
@@ -319,6 +325,6 @@
</div>
<div class="toast hidden" id="toast"></div>
<script src="app.js?v=18"></script>
<script src="app.js?v=19"></script>
</body>
</html>