空间分析基础
Turf.js 不画地图,只做计算。输入几何,输出几何或数字, 所以它可以跑在浏览器、Node、甚至一个没有 DOM 的测试环境里。 把数据模型和单位搞清楚,后面的每个函数都不难。
它解决的是什么问题
前端做 GIS 经常遇到一类需求:判断一个坐标是否落在某个园区里、把一条路线切成等距的点、 统计两个区的重叠面积。这些逻辑如果用经纬度自己写,很快会撞上球面距离、多边形环方向、 坐标顺序这些细节。Turf 把这些算法封装成一个个纯函数,返回值仍然是标准 GeoJSON, 可以直接塞给 Leaflet、MapLibre 或后端接口。
引入与第一个计算
浏览器里用一个 script 标签就够,全局变量是 turf。
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"></script>
<script>
var a = turf.point([116.391263, 39.907359]); // 天安门
var b = turf.point([121.473701, 31.230416]); // 上海人民广场
var km = turf.distance(a, b, { units: 'kilometers' });
console.log(km); // 1067 左右,单位为公里
</script>
turf.point 的第一个参数是 [经度, 纬度]。
顺序写反不会报错,只会算出一个位于南极附近的荒谬结果。
GeoJSON 是唯一的数据接口
所有函数都接受 GeoJSON 对象。三种常见形态都能用:
| 形态 | 示例 | 说明 |
|---|---|---|
| Geometry | { type: 'Point', coordinates: [116.39, 39.90] } |
只有几何,没有属性;距离、面积这类函数直接用得上 |
| Feature | { type: 'Feature', properties: {}, geometry: {...} } |
地理要素的标准形态,绝大多数函数的返回类型 |
| FeatureCollection | { type: 'FeatureCollection', features: [...] } |
批量统计、网格、聚合函数的常见输入 |
多数函数对 Geometry 和 Feature 都做了兼容,但少数只接受 Feature。
统一用 turf.feature() 包一层,可以免去在不同版本间试错的时间。
var geom = { type: 'Point', coordinates: [116.391263, 39.907359] };
var feat = turf.feature(geom, { name: '天安门' });
console.log(feat.properties.name); // 天安门
构造要素的四个常用函数
var p = turf.point([116.39, 39.90], { name: 'A' });
var l = turf.lineString([[116.39, 39.90], [116.42, 39.92], [116.46, 39.94]]);
var poly = turf.polygon([[
[116.35, 39.86], [116.48, 39.86], [116.48, 39.96],
[116.35, 39.96], [116.35, 39.86] // 首尾必须相同
]]);
var fc = turf.featureCollection([p, l, poly]);
多边形的每个环都要闭合:第一个坐标和最后一个坐标完全一致。
漏掉闭合点,turf.area 仍会算出一个数,但结果偏小,
而 turf.booleanPointInPolygon 在边界附近的判断会变得不稳定。
单位:默认公里,面积恒为平方米
这是 Turf 里最容易出错的一处约定。带距离概念的函数默认用公里,
通过 options.units 可以改成 meters、
miles、nauticalmiles;而 turf.area
没有 units 参数,返回值永远是平方米。
| 函数 | 默认输出单位 | 是否可改 |
|---|---|---|
distance | 公里 | 可以,units |
length | 公里 | 可以,units |
buffer 的半径 | 公里 | 可以,units |
area | 平方米 | 不行,始终是平方米 |
bbox | 度(经纬度数值) | 不行 |
把平方米换算成平方公里要除以 1e6,换公顷除以 1e4。
写成 turf.area(poly) / 1000000 比记单位换算表更不容易错。
球面计算,不是平面像素
Turf 的距离基于 WGS84 球面上的大圆距离,面积基于球面多边形, 所以同一块区域的面积不会随地图缩放或投影变化。 这也意味着它和按 Web 墨卡托平面量出来的结果对不上,尤其在高纬度。 用栅格切片统计出来的面积与 Turf 结果有差异时,先确认统计所用的坐标系, 而不是怀疑算法本身。
精度不是无限的。球面公式把地球当正球体,长距离测距与椭球体结果相差千分之几。 要求毫米级时应当用投影后的平面坐标配合专业库,Turf 的定位是前端交互与快速判断。
结果怎么校验
计算完不要直接上屏。两类检查成本最低:
- 坐标范围:经度应在 -180 到 180,纬度在 -90 到 90,越界基本是顺序写反。
- 量级:城市级距离是几十公里,省级是几百公里,跨国是几千公里,数量级明显不对就先查单位。
function safeDistance(a, b) {
var km = turf.distance(a, b, { units: 'kilometers' });
if (!isFinite(km) || km <= 0) return null;
return Math.round(km * 1000); // 统一成米,避免上层再猜单位
}
常见错误
把 [lat, lng] 传给 Turf
Leaflet 的 L.latLng、map.getCenter() 返回的是纬度在前。
直接把 [lat, lng] 交给 Turf,会把北京算到印度洋。
转换用 [lng, lat] 显式重排,不要依赖隐式转换。
在循环里反复构造 Feature
一万个点逐个调用 turf.point 再判断,对象分配的开销会超过计算本身。
先把坐标数组准备好,批量处理时只构造一次 FeatureCollection。
把 undefined 当成 false
函数名拼错时 turf.xxx 是 undefined,调用会抛错;
而拼错的属性用在条件里会静默得到假值,页面看起来「判断永远不成立」。
引入后先确认函数存在,再处理业务逻辑。
经纬志(2026)。《空间分析基础》。https://www.jingweizhi.com/turf/tutorial/basics.html(访问日期 2026-11-03)。