heightCache.js 2.6 KB

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