几何运算:相交、合并与差集
面面运算是 Turf 里结果最不可预测的一组函数。
输入合法时它们给出标准 GeoJSON,输入有瑕疵时会返回 null
或者带细碎缝隙的面。理解这一点,比记住参数更重要。
三个核心函数
| 函数 | 语义 | 不相交时 |
|---|---|---|
turf.intersect(a, b) | A 与 B 的公共部分 | 返回 null |
turf.union(a, b) | A 与 B 合并后的整体 | 返回 MultiPolygon |
turf.difference(a, b) | A 去掉与 B 重叠的部分 | 返回 A 本身 |
var a = turf.polygon([[[116.35, 39.86], [116.45, 39.86], [116.45, 39.94],
[116.35, 39.94], [116.35, 39.86]]]);
var b = turf.polygon([[[116.40, 39.90], [116.50, 39.90], [116.50, 39.98],
[116.40, 39.98], [116.40, 39.90]]]);
var inter = turf.intersect(a, b);
console.log(inter.geometry.type); // Polygon
console.log(turf.area(inter) / 1e6); // 平方公里
var merged = turf.union(a, b);
console.log(merged.geometry.type); // Polygon,相邻时为单面
var rest = turf.difference(a, b);
console.log(turf.area(rest) / 1e6);
intersect 返回 null 不是异常
两个面完全分离、只共享一条边、或者几何退化时,intersect
返回 null,而不是空 Polygon。调用处必须判空,
否则下一行读 result.geometry 就会抛
Cannot read properties of null。
var inter = turf.intersect(a, b);
var areaKm2 = inter ? turf.area(inter) / 1e6 : 0;
只共享边界的情况值得单独注意:从几何角度公共部分是一条线,
面积为 0,而 Turf 返回 null。业务上如果需要区分
「完全分离」和「贴边」,先用 turf.booleanIntersects 判断,
再决定怎么展示。
环方向与自相交
拓扑运算依赖多边形环的合法性。两个问题最常见:
- 环不闭合:首尾坐标不一致,运算可能返回不完整的面。
- 自相交:轮廓线自己交叉,形成「8」字形,多数库对这种输入的行为不一致。
自相交可以用 turf.kinks 提前发现,它返回所有交叉点;
环方向可以用 turf.rewind 统一,外环逆时针、内环顺时针。
这两个函数不修复几何,只做检查和规范化。
var kinks = turf.kinks(polygon);
if (kinks.features.length) {
console.log('存在自相交,交叉点数量:', kinks.features.length);
}
var fixed = turf.rewind(polygon, { reverse: false });
真实数据里带自相交的面大多来自手绘或格式转换。与其在运算阶段反复试, 不如在入库时用桌面工具做一次拓扑检查。
结果里为什么有细碎顶点
面面运算会把两条边的交点插入结果,即使交点落在一条边的中间。
因此输出的顶点数通常多于输入之和,还可能产生面积很小但非零的碎片。
用 turf.cleanCoords 去掉重复点,用面积阈值过滤碎片:
var cleaned = turf.cleanCoords(merged);
/* 过滤掉小于 1 平方米的碎片,避免图上一堆几乎看不见的小面 */
function dropSlivers(feature, minArea) {
if (feature.geometry.type !== 'MultiPolygon') return feature;
var polys = feature.geometry.coordinates.filter(function (coords) {
return turf.area(turf.polygon(coords)) >= minArea;
});
return polys.length
? turf.multiPolygon(polys, feature.properties)
: null;
}
属性不会自动合并
union 与 intersect 只处理几何,属性取自第一个输入,
第二个输入的属性直接丢失。需要保留双方信息时,在调用前自己拼好:
var props = {
aName: a.properties.name,
bName: b.properties.name
};
var merged = turf.union(a, b);
merged.properties = props;
线、点不要用面函数
intersect 只处理面。线线求交用 turf.lineIntersect,
线被面切割用 turf.lineSplit,点落在面上用
turf.booleanPointInPolygon。把线塞进面函数通常得到
null 或类型错误,而不是想要的结果。
版本差异要留意。7.x 的拓扑运算基于 polygon-clipping,
对 MultiPolygon 和带洞面的支持比 6.x 完整;从旧项目升级时,
同类输入可能从 null 变成正常结果,回归测试要覆盖这类边界数据。
常见错误
用面积为零判断相交失败
浮点误差会让本应为 0 的面积算出一个极小正数。判断是否相交,
用 intersect 是否为 null,
不要用面积阈值去猜。
把 difference 的方向搞反
turf.difference(a, b) 是「A 减去 B」。
参数顺序反了会得到完全不同的形状,而且两个结果都合法,不会报错。
运算前没统一坐标系
一边是 WGS84 经纬度,一边是投影后的米制坐标,运算结果毫无意义。 Turf 不检查坐标系,它只把数字当经纬度处理。
经纬志(2026)。《几何运算:相交、合并与差集》。https://www.jingweizhi.com/turf/tutorial/geometry-ops.html(访问日期 2026-11-03)。