本文介绍空间分析库Turf.js的接口,并给出实例。
知识点
Turf .js是空间分析的 JavaScript 库。它包含传统的空间操作、用于创建 GeoJSON 数据的辅助函数以及数据分类和统计工具。其涵盖空间几何图形的测量、关系判断、坐标变换和偏移、辅助方法等具体场景,功能强大。
因为Turf.js使用GeoJSON格式处理所有地理数,因此其坐标顺序为经度,纬度,与leaflet刚好相反。
Turf.js 不仅能在后端的Node.js环境中使用,也可在浏览器环境中使用,比如与 Leaflet、MapBox GL JS 等地图库配合。本文基于原来的综合示例,学习一些入门级的接口,由于是在html中调用接口,所以给出实例和对应展示效果。详细接口说明建议参考官网。
实践
下载和使用
参考https://turf.nodejs.cn/docs/getting-started文档下载,编写本文时(2025年11月),最新版本为7.2.0,具体地址:https://unpkg.com/@turf/turf@7.2.0/turf.min.js。
在html文件引入:
<script src="mymap/maptool/turf-7.2.0.min.js"></script>
点、线、面
turf.js的点、线、面,与leaflet有较多相似之处。
点:turf.point([坐标], {可选属性});。
线:turf.lineString([坐标], {可选属性})。
面:turf.polygon([坐标], {可选属性})。
核心代码:
const points = [
[108.397293,22.738823],
[108.481064,22.724890]
];
// 点
// 注:turf.point 返回点,第二个参数可指定properties参数,drawOneLayer 函数会自动判断name,故加之
const point1 = turf.point(points[0], {name: "五象湖公园"});
var pointLayer1 = drawOneLayer(point1);
addToLayers(pointLayer1);
const point2 = turf.point(points[1], {name: "南宁园博园"});
var pointLayer2 = drawOneLayer(point2);
addToLayers(pointLayer2);
// 线
const linePoints = [
[108.316269, 22.838212],
[108.326569, 22.807200],
[108.347168, 22.779347],
[108.352661, 22.759720],
[108.379440, 22.738190]
];
var line = turf.lineString(
// [
// [108.316269, 22.838212],
// [108.326569, 22.807200],
// [108.347168, 22.779347],
// [108.352661, 22.759720],
// [108.379440, 22.738190]
// ],
linePoints,
{color: "#FF00FF", name: "折线"}
);
// 可指定为miles或kilometers,默认后者,可不写
var totalDistance = turf.length(line, { units: "kilometers"});
var lineLayer = drawOneLayer(line);
addToLayers(lineLayer);
// 面
const polygon = turf.polygon(
[
[
[108.3, 22.86],
[108.4, 22.86],
[108.4, 22.9],
[108.3, 22.9],
[108.3, 22.86]
]
], {color: "#0000FF"}
);
var polygonLayer = drawOneLayer(polygon);
addToLayers(polygonLayer);
注意,drawOneLayer和addToLayers是为了显示绘制效果而调用的。效果如下图所示。

里程、面积计算
计算长度:turf.length(line),默认返回长度单位为千米。
计算里程:turf.distance(point1, point2),默认返回长度单位为千米。
计算面积:turf.area(polygon),返回面积单位为平方米。
核心代码:
// 计算两点间距离
const disTurf = turf.distance(point1, point2, { units: 'kilometers' });
info +=`2点间距离(turfs): ${disTurf.toFixed(3)} 公里\r\n`;
const disLeaflet = calcDistance(points, "km")
info +=`2点间距离(leaflet): ${disLeaflet.toFixed(3)} 公里\r\n`;
// 可指定为miles或kilometers,默认后者,可不写
var totalDistance = turf.length(line, { units: "kilometers"});
info += `线段长度: ${totalDistance.toFixed(3)} 公里\r\n`
const disLeaflet2 = calcDistance(linePoints, "km")
info +=`线段长度(leaflet): ${disLeaflet2.toFixed(3)} 公里\r\n`;
// 计算面积
const area = turf.area(polygon); // 平方米
info += `面积: ${area.toFixed(3)} 平方米`;
turf使用distance计算2个经纬度的里程,用length即可计算经纬度数组的里程。leaflet也有类似的接口,其使用distanceTo计算2个经纬度里程,但没有length功能接口,下面实现之:
// 计算经纬度数组points的总里程
function calcDistance(points, units = 'km') {
let totalDistance = 0;
for (let i = 0; i < points.length - 1; i++) {
const [lng1, lat1] = points[i];
const [lng2, lat2] = points[i + 1];
const distance = L.latLng(lat1, lng1).distanceTo(L.latLng(lat2, lng2));
totalDistance += distance;
}
// 单位转换
const unitConvert = {
'm': (d) => d,
'km': (d) => d / 1000,
'mile': (d) => d / 1609.34
};
const convert = unitConvert[units] || unitConvert['km'];
return convert(totalDistance);
}
效果图如下:
可以看到,使用turfs和leaflet计算相同经纬度里程的结果是相同的。

外接矩形、中心点
计算最小外接矩形:turf.bbox。
计算中心点:turf.center。
核心代码:
function turf_demo2() {
var info = ""
// 最小外接矩形
var bbox = turf.bbox(gxGeoJson); // gxGeoJson 在 450000-广西壮族自治区.js 中定义
var polygon = turf.bboxPolygon(bbox, {}, {color: "#0000FF"});
var polygonLayer = drawOneLayer(polygon);
addToLayers(polygonLayer);
info += `广西最小外接矩形为 ${bbox}\r\n`
// 计算中心点
const center = turf.center(gxGeoJson);
const latlng = center.geometry.coordinates;
const point = turf.point(latlng, {name: "中心点"});
var pointLayer = drawOneLayer(point);
addToLayers(pointLayer);
info += `中心点为 ${latlng}\r\n`
// 计算总面积
var totalArea = turf.area(gxGeoJson);
totalArea = totalArea / 10000000000;
info += `广西总面积 ${totalArea.toFixed(3)} 万平方千米`
return info
}
效果图:

根据GeoJSON使用turf.area计算总面积,为23.81万平方千米,与官方部门提供的数据(23.76万平方千米)非常接近。
贝塞尔曲线Bezier
调用turf.bezierSpline可以将一条线段变成平滑的塞尔曲线。
核心代码:
// 坐标点
var points = [
[108.390427,22.922982],
[108.476257,22.929306],
[108.524323,22.895786],
[108.500290,22.841376]
];
// 原始线
var line = turf.lineString(
points,
{color: "#0000FF", name: "折线"}
);
addToLayers(drawOneLayer(line));
var line1 = turf.lineString(
points,
);
var curved = turf.bezierSpline(line1,
{
properties: {color: "#FF0000", name: "折线"}
}
);
addToLayers(drawOneLayer(curved));
效果图如下,其中蓝色为原始线段,红色为贝塞尔曲线。

根据官方手册,可带resolution和sharpness参数。值越高,效果越好,下面代码将参数调低数值。
var curved = turf.bezierSpline(line1,
{
resolution: 1000,
sharpness: 0.5,
properties: {color: "#FF0000", name: "折线"}
}
);
addToLayers(drawOneLayer(curved));
效果比默认的差一些,如图:

空间关系判断
点与线关系的判断:turf.booleanPointOnLine,点在线上,返回true。
点与面关系的判断:booleanPointInPolygon,点在面内,返回true。
面与面关系的判断:booleanOverlap,如有重叠,返回true。
为减少篇幅,只列出核心代码,完整代码参考文后工程仓库代码。
点线关系判断代码片段:
const points = [
[108.481064,22.724890],
[108.397293,22.738823]
];
const point1 = turf.point(points[0], {name: "南宁园博园"});
addToLayers(drawOneLayer(point1));
const point2 = turf.point(points[1], {name: "五象湖公园"});
addToLayers(drawOneLayer(point2));
var line1 = turf.lineString(
[
[108.481064,22.724890],
[108.397293,22.738823],
[108.369827,22.774599]
], {color: "#0000FF"}
);
addToLayers(drawOneLayer(line1));
// 判断点与线的关系
const isPointOnLine1 = turf.booleanPointOnLine(point1, line1);
// 不在线上的点
const outPoint = turf.point([108.45,22.77], {name: "其它点"});
addToLayers(drawOneLayer(outPoint));
const isPointOnLine2 = turf.booleanPointOnLine(outPoint, line1);
info += `点1与线1关系:${isPointOnLine1}, 其它与线1关系: ${isPointOnLine2}`;
效果:

点面关系判断代码:
polygon1 = turf.polygon(
[
[
[108.3, 22.86],
[108.4, 22.86],
[108.4, 22.9],
[108.3, 22.9],
[108.3, 22.86]
]
], {color: "#0000FF"}
);
polygon2 = turf.polygon(
[
[
[108.349228,22.918555],
[108.378067,22.963451],
[108.476257,22.950806],
[108.479004,22.893888],
[108.440552,22.855929],
//[108.417892,22.909701], // 删除此坐标,两多边形相交
[108.349228,22.918555]
]
], {color: "#0000FF"}
);
addToLayers(drawOneLayer(polygon1));
addToLayers(drawOneLayer(polygon2));
inPoint = turf.point([108.304596,22.895786], {name: "内点"})
addToLayers(drawOneLayer(inPoint));
const isPointInPoly = turf.booleanPointInPolygon(inPoint, polygon1);
const isOverlap = turf.booleanOverlap(polygon1, polygon2);
info += `点与面关系:${isPointInPoly}, 面与面的关系: ${isOverlap}\r\n`;
效果:

几何运算
交集:turf.intersect
并集:turf.union
差集:turf.difference
上述几个函数均返回GeoJson格式,可直接用之显示在地图上。
核心代码:
polygon1 = turf.polygon(
[
[
[108.3, 22.86],
[108.4, 22.86],
[108.4, 22.9],
[108.3, 22.9],
[108.3, 22.86]
]
], {color: "#0000FF"}
);
polygon2 = turf.polygon(
[
[
[108.325195,22.887562],
[108.327942,22.872379],
[108.422012,22.864155],
[108.446732,22.879971],
[108.417892,22.891990],
[108.325195,22.887562]
]
], {color: "#0000FF"}
);
// 交集
const intersection = turf.intersect(turf.featureCollection([polygon1, polygon2]), {properties: {color: "#FF0000"}});
// 并集
const union = turf.union(turf.featureCollection([polygon1, polygon2]), {properties: {color: "#008000"}});
// 差集
const difference = turf.difference(turf.featureCollection([polygon1, polygon2]), {properties: {color: "#FF0000"}});
效果如下所示,从上而下依次是原始2个多边形,交集、并集、差集。

需要说明的是,笔者之前使用的是turf6版本,当前使用turf7版本,两版本之间部分接口参数是不同的。以下交集接口的对比:
v6版本:
const intersection = turf.intersect(polygon1, polygon2);
v7版本:
const intersection = turf.intersect(turf.featureCollection([polygon1, polygon2]));
发现这些变化,也是经历了一些曲折的过程。由于笔者旧的工程有使用计算2个多边形交集的接口,更新turf版本后抛出异常,查了很久才发现是接口参数变化了。
小结
turf还有很多其它高阶功能,但GIS于笔者而言,只是工作的一小部分而已,目前使用到的函数,只限于其中一小部分,比如,计算外接矩形、多边形交集,等。其它接口,留待后续有需要时再研究了。
代码
文中列出了主要的代码片段,另有相关的工程demo,已上传到github仓库。因不定时更新,代码不一定与文中严格对应,以代码仓库为准。如使用,请自行根据实际情况修改。
仓库地址:https://github.com/latelee/mapdemo 。
本文涉及文件:100.综合示例.html,函数为turf_demo1、turf_demo2。
附
- turf官方API说明(英文版):https://turfjs.org/docs/api/along
- turf官方文档:http://turfjs.org/docs/getting-started
- turfAPI说明(中文版但较旧):https://turf.nodejs.cn/docs/api/along