Files
gmTouringMiniApp/src/stores/modules/map.ts
T
周瑞哲 377efbffa8
AI Code Review / review (pull_request) Successful in 7m5s
feat(map): 初始视野对到点位聚类中心,并在已授权时开屏静默定位
初始视野
- 新增 computeClusterViewport:把全部点位当一个聚类,中心取算术中心
  (跟点位密度走,不像外接矩形中心那样被个别远点拽偏),scale 由聚类跨度
  反推出「一眼看全」的档位。当前 30 个点位算出 (113.92754, 22.76105)
  scale 12 —— 原来的 scale 11 会把光明区缩成一小块,留一圈空白。
- 视野改成运行时实时算,不再读数据集里手写的 defaultViewport:增删点位之后
  手写值会过期,算出来的不会。手写值降级为点位为空时的兜底。
- 「回到全域」用同一个聚类视野。
- loadDataset 里判断「视野还没被用户动过」原来是硬编码 113.935/22.748/11
  三个字面量,跟 DEFAULT_GUANGMING_VIEWPORT 重复;改成直接跟常量比。

开屏定位
- 新增 restoreLocationOnLaunch:仅当 scope.userLocation 已授权时静默定位并
  居中,蓝点直接出现在用户位置上。未决定/已拒绝一律不碰,避免小程序第一帧
  就弹微信授权框、拒绝后再连弹一个引导框;那两种状态留给定位按钮。
- 静默定位只在用户确实在光明区包络内才居中,否则把地图甩到没有任何点位的
  地方比停在聚类视野更糟。
- 静默失败不弹提示,但仍写入终态,否则 status 卡在 locating、定位按钮
  永远显示「定位中…」。
- 新增 isWithinGuangmingArea,与数据校验共用同一个包络定义。

需要说明:地图漂到几内亚湾不是「开屏没请求定位」造成的,而是没有 fix 时
(0, 0) 被写进 viewport。堵住它的是 isTrustworthyCoordinate(7ef4094),
开屏定位是叠在守卫之上的体验改进,不是替代 —— 用户拒权、模拟器没设位置、
室内超时都还是拿不到 fix。
2026-08-03 12:14:46 +08:00

72 lines
2.2 KiB
TypeScript

import type { MapViewport } from '@/domain/poi'
import { defineStore } from 'pinia'
import { isTrustworthyCoordinate } from '@/domain/poi'
/**
* 冷启动第一帧的视野,只在数据集读出来之前生效。
*
* 取值是当前数据集全部点位的聚类中心与「一眼看全」的缩放(见 computeClusterViewport)。
* 数据集一加载完,地图页会用实时算出来的聚类视野覆盖它,所以这里稍微过期也不影响,
* 但对齐能让第一帧就落在光明区,不会出现「先在别处、再跳过来」的位移。
*/
export const DEFAULT_GUANGMING_VIEWPORT: MapViewport = {
longitude: 113.9275,
latitude: 22.761,
scale: 12,
}
interface MapSessionState {
categoryCode: string | null
selectedPoiId: string | null
viewport: MapViewport
plannedRouteVisible: boolean
}
function isUsableViewport(viewport: MapViewport): boolean {
return isTrustworthyCoordinate(viewport.longitude, viewport.latitude)
&& Number.isFinite(viewport.scale)
&& viewport.scale > 0
}
export const useMapStore = defineStore('map-session', {
state: (): MapSessionState => ({
categoryCode: null,
selectedPoiId: null,
viewport: { ...DEFAULT_GUANGMING_VIEWPORT },
plannedRouteVisible: false,
}),
actions: {
setCategory(categoryCode: string | null) {
this.categoryCode = categoryCode
},
selectPoi(poiId: string | null) {
this.selectedPoiId = poiId
},
updateViewport(viewport: MapViewport) {
if (!isUsableViewport(viewport))
return
this.viewport = { ...viewport }
},
resetViewport(viewport: MapViewport = DEFAULT_GUANGMING_VIEWPORT) {
this.viewport = isUsableViewport(viewport)
? { ...viewport }
: { ...DEFAULT_GUANGMING_VIEWPORT }
},
showPlannedRoute() {
this.plannedRouteVisible = true
},
hidePlannedRoute() {
this.plannedRouteVisible = false
},
resetSession(viewport: MapViewport = DEFAULT_GUANGMING_VIEWPORT) {
this.categoryCode = null
this.selectedPoiId = null
this.viewport = isUsableViewport(viewport)
? { ...viewport }
: { ...DEFAULT_GUANGMING_VIEWPORT }
this.plannedRouteVisible = false
},
},
})