Turf.js多边形如何生成等距线?手把手教你GIS空间插值实战(附:代码示例)
Turf.js多边形如何生成等距线?手把手教你GIS空间插值实战(附:代码示例)这篇文章解决一个很常见的 WebGIS 问题:前端只有一批带数值属性的点,想在浏览器里生成等值线,并把结果裁剪到研究区多边形范围内,用于展示温度、降雨、污染浓度、地价、人口密度等连续空间现象。
这里先说明一个容易混淆的概念:Turf.js 本身更擅长做矢量空间分析,例如缓冲区、相交、裁剪、合并、距离计算等;严格意义上的“空间插值”不是 Turf.js 的核心强项。不过,在前端实战中,我们可以用“点数据栅格化 + Turf.js isolines 生成等值线 + 多边形裁剪”的方式,完成一套轻量级的 GIS 空间插值可视化流程。
引言:Turf.js多边形生成等距线适合什么场景
很多同学搜索“Turf.js多边形生成等距线”时,其实想做的是“在一个行政区、地块或研究区多边形内,根据采样点生成等值线”。例如:
- 根据气象站点温度生成温度等值线;
- 根据监测点污染浓度生成污染扩散示意图;
- 根据高程点生成简单的等高线示意;
- 根据商圈评分点生成热度分布线;
- 在 WebGIS 页面中动态展示分析结果,而不是提前在桌面 GIS 中制图。
本文会用一个完整流程演示:准备点数据和多边形边界,生成插值网格,使用 Turf.js 生成等值线,再把等值线裁剪到多边形范围内。代码以浏览器环境为主,也适合和 Leaflet、Mapbox GL JS、OpenLayers 等 WebGIS 框架结合。

背景:为什么不能直接对多边形生成等距线
在 GIS 里,“等距线”这个说法经常有两种含义:
- 等值线:连接相同数值的线,例如等高线、等温线、等降雨量线;
- 等距离线:与某个边界保持固定距离的线,更接近缓冲区或内缩缓冲区。
本文讨论的是前一种,也就是常见的 Turf.js isolines 等值线。它需要输入一组带有数值属性的点或网格点,例如每个点都有一个 value 字段。Turf.js 不能凭空从一个普通多边形生成等值线,因为普通多边形只有边界,没有连续变化的数值面。
因此,正确的数据逻辑应该是:
- 研究区多边形负责限定分析范围;
- 采样点负责提供空间数值;
- 插值或格网化过程负责把离散点转换为可生成等值线的规则点集;
- Turf.js 的
isolines根据数值字段生成等值线; - 最后用多边形裁剪结果,只保留研究区内部的线。
原理:Turf.js空间插值生成等值线的核心思路
Turf.js空间插值在实际项目中通常分为两步:先构造规则网格,再为每个网格点计算一个数值。Turf.js 提供了 interpolate 方法,可以根据已知点的属性值,为网格点估算数值。常见参数包括:
- gridType:生成的格网类型,例如
points、square、hex、triangle; - property:用于插值的字段名,例如
value; - units:网格间距单位,例如
kilometers; - weight:距离权重,值越大,近距离点对结果影响越强。
得到插值网格点后,再使用 turf.isolines 根据指定分级值生成等值线。例如分级值为 [10, 20, 30, 40],结果会生成对应数值等级的线要素。
注意:Turf.js 的前端插值适合轻量级展示和交互分析。如果你要做严肃的地统计建模,例如克里金插值、半变异函数拟合、大范围高精度栅格分析,建议使用 QGIS、ArcGIS Pro、Python GIS 或 PostGIS Raster 等后端流程。
步骤:使用 Turf.js 生成多边形范围内等值线
步骤一:准备研究区多边形和采样点
下面示例使用 GeoJSON 数据。研究区是一个简单多边形,采样点带有 value 字段。实际项目中,你可以把它们替换为接口返回的数据、数据库查询结果或用户上传的 GeoJSON。
const boundary = {
"type": "Feature",
"properties": { "name": "研究区" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[116.30, 39.85],
[116.55, 39.85],
[116.55, 40.05],
[116.30, 40.05],
[116.30, 39.85]
]]
}
};
const samplePoints = {
"type": "FeatureCollection",
"features": [
turf.point([116.34, 39.88], { value: 12 }),
turf.point([116.40, 39.90], { value: 18 }),
turf.point([116.48, 39.89], { value: 25 }),
turf.point([116.36, 39.98], { value: 30 }),
turf.point([116.45, 40.01], { value: 38 }),
turf.point([116.52, 39.97], { value: 42 })
]
};
这里的关键是:每个点必须有同一个数值字段,例如 value。如果字段名不统一,后面的 interpolate 和 isolines 都无法得到正确结果。
步骤二:计算多边形外包矩形
插值通常需要一个范围。最简单的做法是使用研究区多边形的外包矩形作为计算范围,然后在最后一步裁剪到多边形内部。
const bbox = turf.bbox(boundary);
// bbox 格式:[minX, minY, maxX, maxY]
console.log(bbox);
如果你的研究区很不规则,外包矩形会包含大量无效区域。小范围项目一般可以接受;如果范围较大,建议先在后端做更精细的栅格化或分块处理。
步骤三:使用 Turf.js interpolate 生成插值点网格
下面使用 turf.interpolate 生成点类型网格。网格间距越小,等值线越平滑,但前端计算量也越大。
const cellSize = 2; // 单位:公里
const interpolatedGrid = turf.interpolate(samplePoints, cellSize, {
gridType: "points",
property: "value",
units: "kilometers",
weight: 2
});
console.log(interpolatedGrid);
参数选择建议如下:
- 城市级小范围展示,可从
1到5公里尝试; - 地块级或园区级展示,需要更小间距,但要注意经纬度坐标下的单位换算;
weight可先使用2,如果结果受近点影响过强,可适当降低;- 如果采样点太少,插值结果只适合示意,不适合严肃分析。
步骤四:生成 Turf.js isolines 等值线
接下来指定等值线分级。分级值应该根据数据最小值、最大值和业务含义来确定,不建议随便写。
const breaks = [10, 15, 20, 25, 30, 35, 40];
const isolines = turf.isolines(interpolatedGrid, breaks, {
zProperty: "value"
});
console.log(isolines);
如果你发现等值线为空,优先检查三件事:
interpolatedGrid中是否真的有value字段;breaks是否落在数据值域范围内;- 输入点是否足够覆盖研究区,而不是集中在一个角落。
步骤五:把等值线裁剪到多边形范围内
Turf.js 的线面裁剪能力不像桌面 GIS 那样完整稳定。对于简单场景,可以遍历等值线,判断线段与研究区的关系;对于复杂多边形,建议使用更专业的裁剪库或后端 GIS 服务。
一个实用的前端简化方案是:先保留与多边形相交的等值线,再在视觉上叠加多边形遮罩或边界。示例代码如下:
const clippedLineFeatures = [];
isolines.features.forEach((line) => {
const intersects = turf.booleanIntersects(line, boundary);
if (intersects) {
clippedLineFeatures.push(line);
}
});
const resultIsolines = turf.featureCollection(clippedLineFeatures);
console.log(resultIsolines);
这个方案是“筛选相交线”,不是严格意义上的几何裁剪。如果你必须把线精确切断在多边形边界上,可以考虑:
- 在后端使用 PostGIS 的
ST_Intersection; - 使用 QGIS 或 ArcGIS Pro 预处理等值线;
- 将等值线结果提交到后端,由 GEOS、GDAL 或 Python Shapely 完成裁剪;
- 前端只负责渲染最终 GeoJSON。
步骤六:在 WebGIS 地图中渲染结果
如果你使用 Leaflet,可以把结果直接加入地图。下面是一个简化示例:
L.geoJSON(resultIsolines, {
style: function(feature) {
const value = feature.properties.value;
return {
color: "#e65100",
weight: 2,
opacity: 0.9
};
},
onEachFeature: function(feature, layer) {
layer.bindPopup("等值线:" + feature.properties.value);
}
}).addTo(map);
L.geoJSON(boundary, {
style: {
color: "#333333",
weight: 2,
fillOpacity: 0
}
}).addTo(map);
如果你使用 OpenLayers 或 Mapbox GL JS,思路也是一样:把 resultIsolines 作为 GeoJSON 数据源,然后配置线样式即可。
常见坑:Turf.js多边形生成等距线失败的原因
1. 把普通多边形当成插值输入
普通多边形没有连续数值字段,不能直接生成等值线。你需要的是带数值属性的采样点,或者已经栅格化的点网格。
2. value 字段不是数字
接口返回的数据经常把数值写成字符串,例如 "25.6"。在插值前建议统一转换:
samplePoints.features.forEach((feature) => {
feature.properties.value = Number(feature.properties.value);
});
如果转换后出现 NaN,说明原字段存在空值、中文、单位符号或其他非法字符。
3. 坐标顺序写反
GeoJSON 坐标顺序是 [经度, 纬度],不是 [纬度, 经度]。坐标写反后,数据可能飞到其他国家,导致等值线无法显示。
4. 等值线分级不在数据范围内
如果数据值范围是 12 到 42,而你设置的分级是 [100, 200, 300],自然不会生成有效等值线。建议先计算最小值和最大值。
const values = samplePoints.features.map(f => f.properties.value);
const minValue = Math.min(...values);
const maxValue = Math.max(...values);
console.log(minValue, maxValue);
5. 网格太密导致浏览器卡顿
前端 Turf.js 空间插值会占用浏览器 CPU。范围越大、网格越密、点越多,计算越慢。实际项目中要控制 cellSize,不要一开始就设置得很小。
6. 误以为 Turf.js 可以替代专业插值模型
Turf.js 适合前端快速分析和可视化,不适合替代完整 GIS 插值建模。如果结果用于规划审批、工程设计、环境评估等严肃场景,建议使用经过验证的专业流程。
方法比较:Turf.js、QGIS、PostGIS 和 Python GIS 怎么选
| 方法 | 适合场景 | 优点 | 限制 |
|---|---|---|---|
| Turf.js | 前端轻量级等值线展示、交互分析、原型验证 | 部署简单,直接在浏览器运行,适合 WebGIS 页面集成 | 复杂插值和精确裁剪能力有限,大数据量容易卡顿 |
| QGIS | 桌面端制图、教学实验、一次性数据处理 | 工具丰富,适合检查数据质量和人工调参 | 不适合高并发在线服务,自动化能力取决于流程设计 |
| PostGIS | 服务端空间查询、结果裁剪、在线接口输出 | 适合和业务数据库结合,空间裁剪和索引能力强 | 栅格插值流程需要额外设计,前端仍需渲染结果 |
| Python GIS | 批处理、科学计算、复杂空间插值 | 可结合 GeoPandas、Rasterio、SciPy、PyKrige 等库 | 需要后端环境,部署和性能优化成本更高 |
如果你的目标是“在网页上快速看到效果”,可以先用 Turf.js。如果你的目标是“得到可审查、可复现、可解释的插值成果”,建议把插值放到 QGIS、Python GIS 或后端服务中完成,前端只负责展示。
检查清单:发布前确认这些问题
- 采样点是否都在合理空间范围内;
- GeoJSON 坐标是否使用
[经度, 纬度]; - 每个采样点是否都有数值型
value字段; - 等值线分级是否落在数据最小值和最大值之间;
- 研究区多边形是否闭合,是否存在自相交;
- 网格间距是否适合当前地图尺度;
- 前端计算是否会阻塞页面交互;
- 等值线是否需要严格裁剪到多边形边界;
- 结果是否只是展示用途,还是需要用于正式分析报告;
- 是否需要在服务端缓存插值结果,避免每次打开页面都重新计算。
FAQ:Turf.js多边形生成等距线常见问题
Q1:Turf.js 可以直接根据多边形生成等值线吗?
不可以。等值线需要连续数值或带数值属性的点网格。多边形只能作为分析范围或裁剪边界,不能单独生成等值线。
Q2:Turf.js isolines 和 interpolate 是什么关系?
interpolate 用来根据采样点估算网格点数值,isolines 用来根据这些网格点生成等值线。前者准备数据面,后者生成线结果。
Q3:为什么我生成的等值线很乱?
常见原因包括采样点太少、点分布不均、异常值没有处理、网格间距不合适、分级设置不合理。建议先在地图上显示采样点,并检查 value 字段是否可信。
Q4:Turf.js 空间插值结果可以用于正式 GIS 分析吗?
一般不建议直接用于正式分析。Turf.js 更适合前端快速展示。正式项目应根据数据特征选择 IDW、克里金、样条函数等合适模型,并记录参数和精度验证过程。
Q5:如何让 Turf.js 等值线只显示在多边形内部?
简单做法是用 booleanIntersects 筛选与多边形相交的线,再通过地图样式突出多边形范围。严格做法是在服务端使用 PostGIS、Shapely 或 GDAL 做几何裁剪。
Q6:前端生成等值线太慢怎么办?
可以增大网格间距、减少采样点数量、限制分析范围、使用 Web Worker 后台计算,或者把插值和裁剪放到后端完成,并缓存结果 GeoJSON。
结论:前端可以做等值线,但要理解边界
用 Turf.js 生成多边形范围内等值线的关键,不是“直接对多边形生成线”,而是建立正确流程:采样点提供数值,多边形限定范围,插值网格承接连续面,isolines 生成等值线,最后再筛选或裁剪到研究区。
对于 WebGIS 原型、数据预览和轻量级专题图,Turf.js多边形生成等距线方案足够实用;但对于精度要求高、数据量大、需要正式交付的 GIS 空间插值任务,建议将核心计算放到 QGIS、Python GIS 或 PostGIS 后端流程中完成。这样既能保证结果质量,也能让前端地图保持流畅。