Leaflet多边形网格填充实战:三种方案对比与完整实现
2026/9/9 22:45:48 网站建设 项目流程

做了两年多WebGIS项目,一直在和Leaflet打交道。前两天项目里有个需求,要把地块多边形用斜纹网格填充,用来区分权属状态。我第一反应是给Polygon加个fillColor就行,结果发现Leaflet的Path只支持纯色填充,根本不给你纹理图案的选项。翻了一圈插件生态,也没有一个专门做“网格填充”的现成方案。最后只能自己动手,把多边形切割成格子,再用L.Rectangle一个个铺上去。这篇文章就是把我踩过的坑、试过的方案和最终的落地代码完整记录下来,给遇到同样需求的同学一条能直接走的路。

1. 网格填充到底解决什么问题:原生Leaflet的边界在哪

先说结论:网格填充不是炫技,是很多业务场景下的硬需求。搞清楚它为什么难做,你才知道后面几条实现路径各自的价值在哪里。

1.1 网格填充的典型应用场景

最典型的是地块权属图。一块地里可能有甲、乙、丙三家的地,纯色填充虽然能区分,但一旦打印成黑白图纸,或者叠加在卫星影像上,视觉区分度就大幅下降。斜线网格、点阵网格这类纹理填充在这种情况下优势非常明显。

第二个场景是区域选中态标注。比如在地图上画一个范围,表示“这个区域正在分析中”,用半透明网格盖在上方,不会完全遮挡底图,又能清楚传达“这里被选中了”的语义。

第三个场景是军标和态势标绘。网格纹理是敌方区域、危险区域标注的通用符号。虽然Leaflet里做军标渲染一般会用专门的渲染库,但如果只是简单标示,网格填充多边形完全够用。

1.2 Leaflet原生fill机制的天然限制

Leaflet的Polygon本质上是L.Path,渲染方式由renderer决定,默认是SVG渲染,也可以切换成Canvas。但无论哪种渲染器,fill能力都局限在单一颜色上,fillColor加fillOpacity,仅此而已。

SVG渲染时,path元素的fill属性看起来可以填url(#patternId),但Leaflet的Path类在内部的_updateStyle方法里会通过setStyle重置样式,你的pattern引用很可能在重绘时被覆盖掉。Canvas渲染器就更不用说了,fillStyle本身就是颜色值,根本没有图案对象的概念。

所以这里有个重要认知:在Leaflet里做网格填充,本质上不是“让Polygon支持图案填充”,而是绕开Polygon的原生渲染,用其他方式来模拟这个效果。

1.3 一条分界线:哪些需求真需要网格,哪些纯色就够了

做技术选型之前先想清楚需求。如果只是要区分区域,纯色加半透明叠加,顶多再加个边框虚线,在很多情况下已经能达到目视区分的要求。网格填充的代价是:要么改渲染机制,要么生成大量子对象,无论哪条路径,都比纯色fill多出不少工作量和性能开销。

我的建议是:如果这个多边形是静态展示的、数量很少的、视觉要求不高的,先别上网格填充。网格填充真正不可替代的场景只有一个——需要在大面积的同色区域上叠加可辨别的纹理。比如几百个小地块,每个地块只有编号文字不同,如果不做纹理填充,用户根本无法快速定位目标地块。

2. 三条实现路线对比:SVG Pattern、网格切分、Canvas自绘

我把网上能找到的实现方案摸了一遍,筛出三条可行的路线。每条路线都有各自的适用边界,我分别拆解一下原理和实现成本。

2.1 方案一:SVG Pattern,改fill属性的取巧路径

原理:Leaflet默认用SVG渲染polygon,在SVG里定义pattern元素,然后让path的fill引用这个pattern id,就能画出斜纹、网格等纹理。思路是在图层add之后,手动找到对应polygon的_path元素,插入pattern定义并修改fill属性。

polygon.on('add', function () { const path = polygon._path; const renderer = map.getRenderer(polygon); const svg = renderer._container; let defs = svg.querySelector('defs'); if (!defs) { defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); svg.appendChild(defs); } const patternId = 'grid-pattern-' + polygon._leaflet_id; const pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern'); pattern.id = patternId; pattern.setAttribute('patternUnits', 'userSpaceOnUse'); pattern.setAttribute('width', '8'); pattern.setAttribute('height', '8'); const line = document.createElementNS('http://www.w3.org/2000/svg', 'path'); line.setAttribute('d', 'M 8 0 L 0 8'); line.setAttribute('stroke', '#666'); line.setAttribute('stroke-width', '1'); pattern.appendChild(line); defs.appendChild(pattern); path.setAttribute('fill', 'url(#' + patternId + ')'); });

这个方案实现简单,改动量很小,渲染效果也非常好,放大缩小不改变纹理密度,看起来是“地图上的区域盖了一层图案”。

但它有两个隐患。第一,Leaflet在调用setStyle或者重新redraw时,可能会把fill属性重置成你之前设置的fillColor,pattern就白搭了。第二,如果多边形数量多,每个polygon都要生成独立的pattern定义,虽然可以共用同一个pattern id,但代码管理上容易乱。这个方案更适合一次性静态展示,不需要动态切换样式的场景

2.2 方案二:网格切分,Leaflet对象组合的思路

原理:把多边形按照固定间距切割成若干小格,判断哪些格子中心或多边形内部,然后把这些格子用L.Rectangle添加到地图上。这个方法完全不触碰Leaflet内部渲染机制,每个格子就是一个合法Leaflet对象,能绑定事件、能参与图层管理,自由度最高。

const rectangles = []; const bounds = polygon.getBounds(); const southWest = bounds.getSouthWest(); const northEast = bounds.getNorthEast(); const gridSize = 0.01; // 经纬度单位,约等于1km const polygonLatLngs = polygon.getLatLngs()[0]; // 外环坐标 for (let lat = southWest.lat; lat <= northEast.lat; lat += gridSize) { for (let lng = southWest.lng; lng <= northEast.lng; lng += gridSize) { const cellCenter = L.latLng(lat + gridSize / 2, lng + gridSize / 2); if (isPointInPolygon(cellCenter, polygonLatLngs)) { rectangles.push( L.rectangle([[lat, lng], [lat + gridSize, lng + gridSize]], { color: '#3388ff', weight: 0.5, fillColor: '#3388ff', fillOpacity: 0.3, interactive: false }) ); } } } const gridLayer = L.layerGroup(rectangles).addTo(map);

这个方案的核心优势是复用Leaflet成熟的对象生命周期,缩放、拖拽、移除都有现成方法,不需要自己写渲染代码。缺点在后面细说,主要就是格子数量大时的性能问题。

2.3 方案三:Canvas自绘,最自由但最费事

原理:扩展L.Layer,自己创建一个Canvas覆盖层,在地图每次viewreset和zoomend时重绘多边形和纹理。纹理部分不再受Leaflet样式系统限制,想画斜线、圆点、蜂窝都可以。

L.GridPatternLayer = L.Layer.extend({ onAdd: function (map) { const canvas = L.DomUtil.create('canvas'); canvas.style.position = 'absolute'; this._canvas = canvas; map.getPanes().overlayPane.appendChild(canvas); map.on('moveend zoomend viewreset', this._redraw, this); this._redraw(); }, _redraw: function () { // 通过map.latLngToContainerPoint把经纬度转成canvas像素坐标 // 绘制多边形外环,再用斜线纹理填充 } });

这个方案在渲染大数据量时最有优势,因为Canvas不需要维护成千上万个DOM节点。适合做那种几千个地块同时网格填充的强交互大屏。缺点是事件系统需要自己实现,点击命中测试要自己写,多边形的增删改也要有对应的数据管理机制,工作量比方案二大不少。

2.4 选型建议:按你自己的场景对号入座

评估维度SVG Pattern网格切分Canvas自绘
实现成本低,几十行代码中,需要算法高,需要管理渲染生命周期
事件支持依附于原polygon每个格子可独立绑定需要手动命中检测
大数据量性能一般,pattern重绘开销较差,格子数量过多会卡最优
纹理类型斜线、网格等SVG pattern只能是色块网格任意图案
动态样式切换容易被Leaflet重置通过setStyle统一修改完全可控

如果只是临时加个纹理,方案一就够了。如果是要做地块管理、业务联动,方案二最平衡。如果数据量上千且纹理要求特殊,老实上Canvas。

3. 网格切分方案的完整实现:从坐标计算到格子生成

方案二最贴近大多数业务项目的需求,我把完整的实现思路和代码展开写。

3.1 射线法判断点面关系:核心算法从零讲解

判断一个点是否在多边形内部,最常用的算法是射线法(Ray Casting)。从目标点发出一条水平射线,计算与多边形每条边的交点个数,如果是奇数,点在多边形内部,否则在外部。这个算法对凸多边形和凹多边形都适用,只要多边形不自相交就行。

function isPointInPolygon(point, polygon) { const [x, y] = point; let inside = false; const len = polygon.length; for (let i = 0, j = len - 1; i < len; j = i++) { const [xi, yi] = polygon[i]; const [xj, yj] = polygon[j]; // 判断点是否在边的y值范围内 const intersects = (yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersects) { inside = !inside; } } return inside; }

这里有几个要注意的细节。第一,(yi > y) !== (yj > y)确保边与射线只有一个交点,处理了顶点落在射线上时的边界情况。第二,分母(yj - yi)不能为0,也就是水平边要被跳过,但上面这个条件天然排除了水平边,因为当yi === yj时,(yi > y) !== (yj > y)是false。第三,这个算法假定射线方向是x轴正向,所以条件里是x < ...,表示交点必须在目标点右边。

我在实际测试中遇到一种情况,格子中心正好落在地物边界上,射线法可能因为浮点误差导致误判。做法是给判断结果做一层容错,或者用格子的四个角判断,只要有一个角在内部就算命中。

3.2 经纬度与投影坐标的选择问题

网格切分第一步是确定网格尺寸。如果直接用经纬度作为网格步长,比如0.01度,在赤道附近大约1.1公里,但在北纬60度地区,同样的经度跨度只有约550米,网格在地图上看起来就会“扁”。

更科学的做法是先把经纬度转成Web Mercator平面坐标,以米为单位切网格,再转回经纬度。Leaflet默认的CRS是EPSG:3857,所以用map.projectmap.unproject就能完成投影转换。

function buildGridInMeters(polygonLatLngs, gridSizeM) { const zoom = map.getZoom(); // 把经纬度数组转成平面坐标数组 const polygonPoints = polygonLatLngs.map(latlng => { const point = map.project(latlng, zoom); return [point.x, point.y]; }); // 计算平面坐标的包围盒 const xs = polygonPoints.map(p => p[0]); const ys = polygonPoints.map(p => p[1]); const minX = Math.min(...xs), maxX = Math.max(...xs); const minY = Math.min(...ys), maxY = Math.max(...ys); // 以米为单位生成网格 const rectangles = []; for (let x = minX; x < maxX; x += gridSizeM) { for (let y = minY; y < maxY; y += gridSizeM) { const center = map.unproject(L.point(x + gridSizeM / 2, y + gridSizeM / 2), zoom); if (isPointInPolygon([center.lat, center.lng], polygonLatLngs)) { const sw = map.unproject(L.point(x, y + gridSizeM), zoom); const ne = map.unproject(L.point(x + gridSizeM, y), zoom); rectangles.push(L.rectangle([sw, ne])); } } } return rectangles; }

注意这里引入了一个隐藏在暗处的变量——zoom。因为经纬度到平面坐标的转换依赖于缩放级别,缩放级别一变,投影坐标就变了。这带来一个问题:如果在zoom级别18切好的网格,用户缩放到zoom级别7,网格位置和大小实际上还是对的(因为L.Rectangle是地理坐标对象,坐标不随缩放改变),但你没法用简单的map.unproject返回结果去重新处理。

我的建议是:用固定zoom级别(比如14)做投影和切分,切完的L.Rectangle坐标直接存数据库或变量,后续操作都基于地理坐标而非投影坐标。这样缩放时只是视觉上格子变大变小,不会出现格子错位。

3.3 生成网格的完整代码:从边界判断到图层添加

把上面两节整合起来,写一个可以直接跑的函数。假设你已经有一个leafletPolygon对象,并且地图实例是全局的map

function generateGridLayer(leafletPolygon, gridSizeM, options = {}) { const latLngs = leafletPolygon.getLatLngs()[0]; // 取外环 const zoom = 14; // 固定投影级别,避免缩放带来的坐标漂移 const polygonPoints = latLngs.map(latlng => { const point = map.project(latlng, zoom); return [point.x, point.y]; }); const xs = polygonPoints.map(p => p[0]); const ys = polygonPoints.map(p => p[1]); const minX = Math.min(...xs), maxX = Math.max(...xs); const minY = Math.min(...ys), maxY = Math.max(...ys); const rectangles = []; for (let x = minX; x < maxX; x += gridSizeM) { for (let y = minY; y < maxY; y += gridSizeM) { const centerPoint = L.point(x + gridSizeM / 2, y + gridSizeM / 2); const centerLatLng = map.unproject(centerPoint, zoom); if (isPointInPolygon([centerLatLng.lat, centerLatLng.lng], latLngs)) { const sw = map.unproject(L.point(x, y + gridSizeM), zoom); const ne = map.unproject(L.point(x + gridSizeM, y), zoom); rectangles.push( L.rectangle([sw, ne], { color: options.color || '#3388ff', weight: options.weight || 1, fillColor: options.fillColor || '#3388ff', fillOpacity: options.fillOpacity || 0.25, interactive: false, ...options.rectangleOptions }) ); } } } return L.layerGroup(rectangles); }

这段代码有几个小坑提前说明。

第一,getLatLngs()返回的是数组的数组,最外层数组每个元素对应一个环。简单多边形只有一个环,所以取[0]。带洞多边形环的索引后面会说。第二,这里用的是中心点判断,对于凹多边形,靠近凹入部分的格子中心可能在多边形外,但实际上有一部分格子面积在多边形内,导致部分边缘格子漏生成。要想更准确,就把网格四个角都测一遍,任意一个角在内部就生成,代价是计算量翻4倍。第三,interactive: false很关键,如果不设,每个rectangle都会参与点击事件命中,事件绑定和性能上都吃亏。

3.4 带洞多边形和MultiPolygon的处理

地理数据里经常有“一块地中间有个湖”这种形状,反映到GeoJSON上就是Polygon带一个或多个inner ring。上面的代码只处理了外环,内环会被错误地填充。

处理方式是在判断时增加一层逻辑:如果点在某个内环内部,则判定为不在多边形内部。

function isPointInPolygonWithHoles(point, rings) { // rings[0] 是外环,其余是内环 if (!isPointInPolygon(point, rings[0])) { return false; } for (let i = 1; i < rings.length; i++) { if (isPointInPolygon(point, rings[i])) { return false; } } return true; }

MultiPolygon更简单,它本质是多个Polygon的集合,对每个Polygon单独做网格切分,然后把结果合并到一个layerGroup里就行。Leaflet的getLatLngs()对MultiPolygon会返回三层嵌套,遍历时注意层数。

4. 性能卡顿的真相:网格数量不是唯一瓶颈

网格切分方案性能问题是最多人踩的坑,我单独用一节来说。

4.1 网格数量与渲染开销的量化关系

一个边长10公里的多边形,用100米网格切分,刚好多边形面积约100平方公里,一个方向100个格,总共约一万个格子。10000个L.Rectangle对象放上去,地图拖拽时的掉帧感非常明显。

问题出在三个层面。第一,DOM节点数:默认SVG渲染器下,每个L.Rectangle都是一个独立path元素,10000个path元素对DOM的增删改查和重绘压力巨大。第二,事件系统:如果每个格子都绑定了事件,Leaflet的事件处理机制会为每个对象注册监听器,内存占用成倍增长。第三,样式更新:当你调用setStyle统一修改网格样式时,Leaflet要遍历每一个path执行属性更新,一次性更新10000个节点的样式必然卡顿。

所以性能优化要从三个方向同时下手。

4.2 水平合并算法:把相邻格子压成Rectangle

这是我自己实现的优化方案,效果非常明显。相邻的格子如果满足“同一行、列号连续”,就可以合并成一个矩形。比如第3行第5列到第10列共六个格子,原来要创建6个L.Rectangle,合并后只需要1个。

function mergeHorizontalCells(cells, gridSizeM) { // cells: [{row, col, sw, ne}] const groupByRow = new Map(); cells.forEach(cell => { if (!groupByRow.has(cell.row)) { groupByRow.set(cell.row, []); } groupByRow.get(cell.row).push(cell); }); const mergedRectangles = []; groupByRow.forEach(rowCells => { rowCells.sort((a, b) => a.col - b.col); let startCol = rowCells[0].col; let prevCol = rowCells[0].col; for (let i = 1; i <= rowCells.length; i++) { if (i === rowCells.length || rowCells[i].col !== prevCol + 1) { const startCell = rowCells.find(c => c.col === startCol); const endCell = rowCells.find(c => c.col === prevCol); mergedRectangles.push( L.rectangle([ [startCell.sw.lat, startCell.sw.lng], [endCell.ne.lat, endCell.ne.lng] ]) ); if (i < rowCells.length) { startCol = rowCells[i].col; } } prevCol = rowCells[i - 1]?.col; } }); return mergedRectangles; }

行合并算法比较麻烦,但收益更大。把每行的矩形再纵向合并成更大的矩形。在规则的多边形里,合并后的对象数量能减少到原来的十分之一甚至几十分之一。对于约10000个格子的多边形,合并后通常只有几百个L.Rectangle,性能问题基本消失。

如果不想自己造轮子,可以考虑用turf.jsturf.hexGrid等现成网格工具,或者用topojson的拓扑压缩思路做简化和合并。但Leaflet项目里我更推荐直接在L.Rectangle层面合并,处理逻辑直接、可调试性强。

4.3 分级网格与缩放联动

当缩放到zoom级别很小时,很多东西都变成一个小像素团,显示那么细的网格没有意义。反过来放大后,原本的网格单元已经大到盖住整个屏幕,网格成为巨大的色块,也不利于阅读。

解决方案是按缩放级别动态调整网格尺寸。zoom在15以上用25米网格,zoom在10-14用100米网格,zoom小于10直接用500米网格。缩放到不同级别时重新生成网格。这个优化不仅让视觉更合理,还大幅减少不必要的格子数量。

map.on('zoomend', function () { const currentZoom = map.getZoom(); let gridSize; if (currentZoom >= 15) gridSize = 25; else if (currentZoom >= 10) gridSize = 100; else gridSize = 500; if (gridSize !== lastGridSize) { map.removeLayer(gridLayer); gridLayer = generateGridLayer(polygon, gridSize); gridLayer.addTo(map); lastGridSize = gridSize; } });

需要加个防抖,不然连续缩放时会频繁重建图层。

4.4 渲染器选SVG还是Canvas的取舍

Leaflet的map可以通过preferCanvas: true选项强制使用Canvas渲染器。Canvas渲染器在大量path对象时性能通常优于SVG,因为不需要维护那么多DOM节点。

但Canvas渲染器有个坑:Canvas虽然能画大量path,但每个path的事件命中检测要遍历所有图形,对象多时拖动和点击都会变卡。而且Canvas不支持单个path的CSS样式调试,浏览器DevTools里看不到DOM结构。

我的建议是:对象数量在2000以下用SVG,2000以上用Canvas。网格切分时如果做了水平合并,一般情况下数量能控制在2000以内,用SVG渲染器足够。

5. 与业务系统集成的细节:点击事件、弹窗、图层顺序

网格不是画出来好看就完事了,要真正接到业务系统里,有几个集成细节特别重要。

5.1 网格与原始多边形的双重事件问题

如果你把原始多边形也保留在地图上,同时又在上面叠加了网格层,点击时网格层会挡住原始多边形的事件。这时候你会遇到两个问题:

  • 点网格时,原始多边形不响应点击;
  • 不同格子click事件的回调里有重复的业务逻辑。

我的做法是去掉原始多边形的交互,把所有事件统一绑定在网格层上

gridLayer.eachLayer(function (rect) { rect.on('click', function (e) { // 通过rect上存储的businessId属性关联业务数据 const businessId = rect.options.businessId; onGridClick(businessId, e); }); });

每个L.Rectangle创建的时候,在options里塞入业务字段,比如地块ID、颜色、标记名称等。事件回调里直接读取这个字段去更新弹窗内容,比每次去全局数据里索引快得多。

5.2 图层分组与pane管理

Leaflet默认的图层顺序是:底图瓦片在最底层,然后是overlayPane(矢量图形),再上面是popupPane和markerPane。如果网格要显示在普通多边形上面、标注下面,就要设置自定义pane。

map.createPane('gridPane'); map.getPane('gridPane').style.zIndex = 450; // 默认overlayPane的zIndex是400 gridLayer.eachLayer(function (rect) { rect.options.pane = 'gridPane'; });

注意:用pane管理后,groupLayer的层级依然存在,但pane决定最终的绘制顺序。网格层放在低zIndex的pane,你的标记和弹窗放高zIndex的pane,这样不会被网格盖住。

5.3 网格的样式切换与业务ID关联

做地块管理,点击不同地块时网格样式要跟着变。比如当前选中的地块网格变成红色,其他地块恢复蓝色。最直接的思路是每个地块的网格单独管理。

const gridLayers = {}; function setActiveBlock(blockId) { Object.keys(gridLayers).forEach(id => { const layer = gridLayers[id]; layer.eachLayer(rect => { rect.setStyle({ fillColor: id === blockId ? '#e74c3c' : '#3388ff', color: id === blockId ? '#c0392b' : '#3388ff' }); }); }); }

这段代码的问题是,eachLayer遍历时如果格子数量大,开销不小。更好的方案是:每个地块的网格单独用一个L.layerGroup存起来,切换时直接对整个group设置样式,甚至直接替换整个group。

6. 踩坑记录与周边的实用技巧

最后把我在实现和部署过程中踩过的坑集中写一下,再补充几个相关但有用的扩展点。

6.1 我只想说一遍的坑

第一个坑是浮点误差导致的格间缝隙。网格切分时,相邻格子的边缘坐标理论上应该完全重合,但浮点运算会产生微小误差,放大地图后格子之间会出现白色细线。解决办法是给每个格子设置weight: 0和透明stroke,让格子只显示填充色,即使有缝隙也看不出来。

第二个坑是网格中心判断与凹多边形边界。中心点在多边形外部、但格子四角都在内部的凹角区域,会被漏判。切分边缘地块时,尽量用“四个角任一在内部则生成”的策略,视觉上更贴近真实边界。

第三个坑是Leaflet setStyle会覆盖pattern引用。如果你用了方案一的SVG Pattern,每次调用setStyle方法都会把fill重置成固定颜色。唯一的解法是重写setStyle逻辑,或者干脆用方案二/方案三。

第四个坑是Web Mercator纬度变形。直接用经纬度做网格间距时,越往北格子看起来越窄,用map.project转平面坐标后切分才能得到正方形网格。

6.2 从网格填充到数据导出:WKT/GeoJSON/SHP的闭环

网格切分出来的L.Rectangle本身就是Leaflet对象,导出GeoJSON非常方便:

const features = []; gridLayer.eachLayer(rect => { features.push(rect.toGeoJSON()); }); const featureCollection = { type: 'FeatureCollection', features: features };

拿到GeoJSON之后,要转成WKT可以用turf.jsturf.helpers.geometryToWkt(或者手动拼接坐标串),要转成SHP可以用shpjs库的shp.write。这样从地图上看到网格,到把网格数据交回业务系统,整个链路是通的。需要注意:如果你在网格矩形里塞了业务字段,导出GeoJSON时需要手动把rect.options里的业务字段复制到properties里,toGeoJSON()默认只导出geometry,不保留options。

6.3 离线环境的部署注意

项目数据涉密不能连外网这种事很常见,leaflet和瓦片都得离线部署。网格切分的算法本身完全在本地跑,不依赖网络,所以离线环境没有任何影响。唯一要注意的是:Leaflet的CSS和JS文件要一起下载到本地,不要只存了JS忘了CSS,不然地图容器会错位。

瓦片源如果离线,可以预先下载瓦片或者使用本地切片工具生成瓦片目录。网格填充用到的坐标投影转换逻辑跟瓦片无关,底图缺失不会影响网格正确性,这点可以放心。

另外一个比较多问的“Leaflet离线加载”问题,其实核心是:

  • leaflet库文件本地化;
  • 瓦片数据本地化或用自定义tileLayer的url指向本地路径;
  • 地理编码、逆地理编码等服务如果离线,需要自建服务或用本地数据集。

我在实际项目里遇到多的是瓦片加载慢,导致网格先渲染、底图后出现,视觉上看起来像网格错位。解决办法是在底图的load事件里再添加网格图层,或者简单粗暴加个setTimeout延迟加载。不过更推荐在baselayerchange时同步重建网格,形成联动。

做完整套方案之后,我回头看这个需求,觉得最核心的经验就一句话:别再试图让Leaflet的原生Polygon支持网格填充了,绕过去用组合逻辑实现,反而更稳、更可控。网格切分方案虽然要写几百行代码,但它给每个格子赋予独立对象身份的能力,是后面做地块交互、样式联动、数据导出的基础。如果你只是在做一个静态的纹理化展示,推荐直接用SVG Pattern方案,简单快速,半小时搞定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询