首页 / 实战手册 / 样式与渲染
多边形的洞方向反了,中间的湖被填满
先看结论
一个带湖的行政区面,渲染后湖面也被涂成了行政区的颜色,卫星底图看不见了。
现象
一个 Polygon,外环是行政边界,内环是湖岸线,共 240 个顶点。
渲染后湖面与外环一样是半透明的蓝色,本应露出的卫星底图被挡住。
在 QGIS 里该要素的拓扑检查正常,面积计算也正确, 说明几何本身没问题,是渲染阶段没有识别出「洞」。
原因
Canvas 与 SVG 的填充规则有两种:
- nonzero(非零环绕):按环的方向累计环绕数,方向相同则累加,洞不会被挖掉。
- evenodd(奇偶):按射线穿越次数判断,与方向无关,洞一定被挖掉。
大多数地图库使用 nonzero,因此要求外环与内环方向相反:
GeoJSON 规范建议外环逆时针、内环顺时针(右手法则)。
两份数据都按顺时针生成时,环绕数不抵消,洞就被填满。
排查过程
- 用鞋带公式算每个环的有向面积,外环与内环符号应相反。
- 若同号,说明方向一致,渲染器无法识别洞。
- 在 QGIS 里用「检查几何有效性」,无效时会提示
Ring Self-intersection或Hole lies outside shell。 - 把填充规则改为
evenodd临时验证,若洞出现,即可确认是方向问题。
function signedArea(ring) {
let s = 0;
for (let i = 0, n = ring.length; i < n - 1; i++) {
s += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
}
return s / 2;
}
signedArea(poly[0]); // 外环 1.2e-3 正
signedArea(poly[1]); // 内环 3.4e-5 正 -> 同号,洞失效
解决方案
渲染前统一绕向:外环逆时针(正面积),内环顺时针(负面积)。 不要依赖数据提供方已经处理好。
function rewindPolygon(rings) {
const out = rings.map((ring, i) => {
const positive = signedArea(ring) > 0;
const wantPositive = i === 0; // 外环为正,内环为负
return positive === wantPositive ? ring : ring.slice().reverse();
});
return out;
}
fc.features.forEach(f => {
if (f.geometry.type !== 'Polygon') return;
f.geometry.coordinates = rewindPolygon(f.geometry.coordinates);
});
Leaflet 的 SVG 渲染器可以直接指定填充规则,作为兜底手段。
L.geoJSON(fc, {
style: { fillOpacity: 0.5, fillColor: '#1a6fd4' },
// SVG 路径默认 nonzero,改成 evenodd 后与绕向无关
onEachFeature: (f, layer) => {
if (layer._path) layer._path.setAttribute('fill-rule', 'evenodd');
}
}).addTo(map);
验证
修正后外环面积为正、内环为负,湖面重新透出底图;
用 turf.area 计算要素面积时,洞会被扣除,结果比修正前小。
console.assert(signedArea(poly[0]) > 0, '外环方向不对');
console.assert(signedArea(poly[1]) < 0, '内环方向不对');
// 带洞面积应小于外环单独面积
console.assert(turf.area(feature) < turf.area({ type: 'Feature', properties: {},
geometry: { type: 'Polygon', coordinates: [poly[0]] } }), '洞未被扣除');
适用版本
| 库 / 规范 | 版本 | 默认填充规则 |
|---|---|---|
| GeoJSON | RFC 7946 | 建议外环逆时针、内环顺时针 |
| Leaflet | 1.9.x | SVG nonzero |
| Mapbox GL JS | 2.15 / 3.x | 内部按绕向处理,方向错时洞失效 |
| Canvas 2D | — | fill('nonzero') 为默认 |
经纬志(2026)。《多边形的洞方向反了,中间的湖被填满》。https://www.jingweizhi.com/cookbook/polygon-hole-winding.html(访问日期 2026-11-03)。