经纬志
首页 / Turf.js / 教程

几何运算:相交、合并与差集

先看结论

面面运算是 Turf 里结果最不可预测的一组函数。 输入合法时它们给出标准 GeoJSON,输入有瑕疵时会返回 null 或者带细碎缝隙的面。理解这一点,比记住参数更重要。

最后验证 2026-11-03 · Turf.js 7.x · 更新 2026-10-27 · 引用本文

三个核心函数

函数语义不相交时
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)。