You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Leaflet中使用H3渲染印度全域六边形网格异常及优化

问题根因
  • H3分辨率设置过高:H3 13级分辨率单六边形平均边长仅约44米,覆盖印度全境会生成超过2000万个单元格,浏览器无法承载这个量级的几何计算和渲染负载,必然出现长时间卡顿、无法加载的问题。
  • 坐标顺序不符合接口规范:h3.polyfill 要求输入坐标遵循GeoJSON标准的[经度, 纬度]格式,代码传入的边界点全是[纬度, 经度]顺序,会导致生成的单元格位置完全偏移,不在印度境内。
  • 渲染逻辑开销过大:每个六边形单独包裹<div>、单独生成独立Polygon组件实例,哪怕仅几千个格子也会产生极高的渲染开销,进一步加剧卡顿。
优化实现方案

核心思路是按需加载、动态适配、批量渲染,从根源降低数据量和渲染开销:

1. 按地图缩放级别动态匹配H3分辨率

不要固定使用13级高分辨率,低缩放级别(全国视角)用低分辨率格子,高缩放级别(街道视角)再切换到高分辨率,参考映射规则:

  • 地图zoom < 5:用H3 res3(单格边长约35km),印度全境仅需约1000个格子
  • zoom 5-7:用H3 res4(边长约13km),全境约7000个格子
  • zoom 7-9:用H3 res5(边长约5km),全境约5万个格子
  • zoom 9-11:用H3 res6(边长约2km),全境约35万个格子
  • zoom 11以上逐步提升分辨率,到res13时用户已经放大到极小的局部范围,仅需加载数百个格子,完全不会卡顿。

2. 仅加载当前地图视野内的格子

不要预生成印度全境所有格子,每次地图平移、缩放结束后,取当前可视区域的边界作为h3.polyfill的输入范围,视野外的格子直接不生成、不渲染,数据量能降低2-3个数量级。

3. 替换为高性能批量渲染

  • 不要为每个六边形生成独立组件、单独包裹DOM节点,把所有格子批量转为GeoJSON FeatureCollection格式,用单个图层一次性渲染
  • 用Canvas渲染器替代默认的SVG渲染器,同量级图形下渲染性能可以提升10倍以上,且不影响点击、hover等交互能力
  • 给渲染逻辑加防抖,避免用户快速拖动/缩放地图时频繁触发计算

参考实现代码

// 缩放级别与H3分辨率映射表,可根据实际视觉效果调整
const ZOOM_TO_H3_RES = new Map([
  [4, 3],
  [6, 4],
  [8, 5],
  [10, 6],
  [12, 7],
  [14, 9],
  [16, 11],
  [18, 13]
]);

function setupHexGrid(map) {
  let currentHexLayer = null;

  const renderHexes = () => {
    // 移除旧图层
    if (currentHexLayer) map.removeLayer(currentHexLayer);
    // 匹配当前缩放级别的H3分辨率
    const currentZoom = map.getZoom();
    let res = 3;
    for (const [zoomThreshold, h3Res] of ZOOM_TO_H3_RES) {
      if (currentZoom >= zoomThreshold) res = h3Res;
    }
    // 获取当前可视区域边界,转成H3要求的[lng, lat]坐标格式
    const mapBounds = map.getBounds();
    const viewRange = [
      [mapBounds.getWest(), mapBounds.getNorth()],
      [mapBounds.getEast(), mapBounds.getNorth()],
      [mapBounds.getEast(), mapBounds.getSouth()],
      [mapBounds.getWest(), mapBounds.getSouth()],
      [mapBounds.getWest(), mapBounds.getNorth()] // 闭合多边形
    ];
    // 仅生成视野内的单元格
    const cellIds = h3.polyfill(viewRange, res, true);
    // 批量构造GeoJSON数据
    const hexGeoData = {
      type: "FeatureCollection",
      features: cellIds.map(cellId => {
        const boundary = h3.h3ToGeoBoundary(cellId, true);
        return {
          type: "Feature",
          properties: { cellId },
          geometry: {
            type: "Polygon",
            coordinates: [[...boundary, boundary[0]].map(([lat, lng]) => [lng, lat])]
          }
        };
      })
    };
    // 用Canvas渲染器批量绘制
    currentHexLayer = L.geoJSON(hexGeoData, {
      renderer: L.canvas(),
      style: {
        color: "#2563eb",
        weight: 1,
        fillOpacity: 0.15
      }
    }).addTo(map);
  };

  // 平移、缩放结束后再触发渲染,避免频繁计算
  map.on("moveend zoomend", renderHexes);
  // 首次加载渲染
  renderHexes();
}

如果使用react-leaflet,直接将构造好的GeoJSON数据传入单个GeoJSON组件即可,不要嵌套循环生成独立的Polygon组件。

最终实现效果参考:
六边形网格效果示例

内容的提问来源于stack exchange,提问作者Premshankar Tiwari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:22:17