| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- /**
- * 地形高程的**短时缓存**(票 98 第二轮)。
- *
- * ## 为什么要缓存
- *
- * DOM 标记层每帧都要把 `(lon, lat)` 投到屏幕,而投影前要先取地面高程
- * (`globe.getHeight`,一次四叉树下降)。标记位置是**静态**的,但相机一动,
- * 每帧都会对**每个标记**重查一遍 —— 几百个标记就是几百次四叉树下降,
- * 这是近景拖动时主线程的主要开销之一。
- *
- * 实体投影那条路径早就按实体缓存了 3 秒(见 `projectMarkerEntity` 的注释);
- * 这里把同一口径用到"按坐标"的调用上,两条路径的语义就一致了。
- *
- * ## 为什么用 TTL 而不是永久缓存
- *
- * 地形瓦片是**逐步补齐**的:早期查不到高程会落到椭球面,补到之后才准。
- * 所以缓存必须过期后重采样,否则标记会一直贴在错的(椭球面)高度上。
- *
- * ## 一条硬纪律
- *
- * **只缓存有效值**。地形还没铺到时 `getHeight` 回 `undefined`/`NaN`,
- * 若把这种"没取到"也缓存 3 秒,标记就会长时间停在椭球面 —— 比不缓存更糟。
- */
- /** 坐标键的默认精度:经纬度 5 位小数约 1m,足够合并同一栋楼上的重复查询。 */
- export const HEIGHT_KEY_PRECISION = 5
- /**
- * 经纬度 → 缓存键。
- * @param {number} lon
- * @param {number} lat
- * @param {number} [precision]
- * @returns {string}
- */
- export function heightKey(lon, lat, precision = HEIGHT_KEY_PRECISION) {
- return Number(lon).toFixed(precision) + ',' + Number(lat).toFixed(precision)
- }
- /**
- * 建一个高程缓存。
- *
- * `read(key, nowMs, produce)`:TTL 内直接给缓存值;否则调 `produce()` 重采样,
- * 且**仅在结果有限时**写回。超过容量就整表清空(宁可重采一轮,也不做 LRU 的额外记账)。
- *
- * @param {object} [options]
- * @param {number} [options.ttlMs] 过期时间(默认 3000,与实体高程缓存同口径)
- * @param {number} [options.maxEntries] 容量上限(默认 5000)
- */
- export function createHeightCache({ ttlMs = 3000, maxEntries = 5000 } = {}) {
- const slots = new Map()
- return {
- read(key, nowMs, produce) {
- const slot = slots.get(key)
- if (slot && nowMs - slot.at < ttlMs) return slot.value
- const value = produce()
- // 只缓存有效值(见文件头):取不到就让它下一帧再试。
- if (Number.isFinite(value)) {
- if (slots.size >= maxEntries) slots.clear()
- slots.set(key, { value, at: nowMs })
- }
- return value
- },
- size: () => slots.size,
- clear() { slots.clear() },
- }
- }
|