- 数据分析
【免费下载链接】turf
A modular geospatial engine written in JavaScript and TypeScript
导读
@turf/line-slice是 Turf(JavaScript/TypeScript 模块化地理空间引擎)中用于"按起止点截取线要素"的核心工具函数:给定一条线和一个起点、一个终点,它返回这条线上位于两点之间的子段(Feature<LineString>)。该模块在路线规划、轨迹回放、路径抽稀等场景中非常实用——例如只需要展示某条导航路线中两个途经点之间的部分。读完本文,你将掌握lineSlice的完整 API、实际调用方式、底层实现原理(基于最近点投影与线段索引排序),以及它的边界处理与测试验证方式。
lineSlice 是什么
lineSlice接收一条 LineString 线、一个起始 Point 点 和一个终止点,返回这条线在这两个点之间的子段。起始点和终止点并不需要恰好落在线上——函数会自动寻找它们在线上最近的投影位置,再截取中间部分。
在 官方 README 中,这一能力被描述为"提取路线上两个途经点之间的部分(extracting only the part of a route between waypoints)",这也是它最典型的应用场景:在完整路线数据中,只保留用户关心的起终点区间。
安装方式
@turf/line-slice是 Turf 模块化架构下的独立包,可以单独安装,也可以随完整版 Turf 一起使用:
# 单独安装本模块 $ npm install @turf/line-slice# 安装包含全部模块(以函数形式挂载到 turf 命名空间下)的完整包 $ npm install @turf/turf从 package.json 可以看到,当前仓库中该模块版本为7.4.0,采用 ESM("type": "module")并声明"node": ">=22"的运行时要求;其运行时依赖仅有三个 Turf 内部包:
@turf/helpers:提供lineString、point、Coord等类型与构造工具;@turf/invariant:提供getCoords、getType等坐标与类型校验工具;@turf/nearest-point-on-line:负责将点投影到线上、寻找最近顶点。
函数签名与参数说明
lineSlice( startPt: Coord, // 起始点 stopPt: Coord, // 终止点 line: Feature<LineString> | LineString // 待截取的线 ): Feature<LineString> // 返回截取后的线| 参数 | 类型 | 说明 |
|---|---|---|
startPt | Coord | 起始点,可以是PointFeature、PointGeometry 或坐标数组 |
stopPt | Coord | 终止点,类型同上 |
line | Feature<LineString>或LineString | 被截取的线要素,支持 Geometry 或 Feature 两种形态 |
返回值始终是一个Feature<LineString>线要素。当输入line是 Feature 时,输出会保留其properties(见下文源码分析);当输入是纯 Geometry 时,输出属性为空对象。
快速上手:官方示例
以下示例来自 README,展示了如何构造一条折线、两个点,然后截取两点之间的子段:
var line = turf.lineString([ [-77.031669, 38.878605], [-77.029609, 38.881946], [-77.020339, 38.884084], [-77.025661, 38.885821], [-77.021884, 38.889563], [-77.019824, 38.892368] ]); var start = turf.point([-77.029609, 38.881946]); var stop = turf.point([-77.021884, 38.889563]); var sliced = turf.lineSlice(start, stop, line); // 用于地图叠加显示 var addToMap = [start, stop, line];注意示例中start(-77.029609, 38.881946)恰好等于线坐标数组的第 2 个顶点,stop(-77.021884, 38.889563)恰好等于第 5 个顶点,因此这里截取的是顶点索引 1 到 4 之间的完整折线段。更常见的情况是两点并不落在顶点上,此时lineSlice会在线上投影出新的端点(见下文"底层原理")。
底层实现原理(源码级解析)
lineSlice的实现位于 index.ts,核心流程清晰,可分为四步:
1. 输入校验
const coords = getCoords(line); if (getType(line) !== "LineString") throw new Error("line must be a LineString");借助@turf/invariant的getCoords与getType先取出坐标并校验几何类型。如果传入的不是 LineString(例如 Polygon、Point 等),会直接抛出line must be a LineString错误,这与文档中"line to slice"的参数约束一致。
2. 寻找最近投影点
const startVertex = nearestPointOnLine(line, startPt); const stopVertex = nearestPointOnLine(line, stopPt);这是整个算法最关键的一步:调用 @turf/nearest-point-on-line,将起止点投影到线上,返回线路上距离最近的点。由于该函数基于逐段求最近点的方式实现,即使输入点偏离线路(如 GPS 轨迹点或手工采集的路口坐标),也能正确投影到线段的合适位置——这正是 README 所说"起止点不一定要恰好落在线上"的原理支撑。
nearestPointOnLine的返回值会携带segmentIndex属性(落在第几个线段上),供下一步排序使用。
3. 按线段索引排序起止点
const ends = startVertex.properties.segmentIndex <= stopVertex.properties.segmentIndex ? [startVertex, stopVertex] : [stopVertex, startVertex];如果终止点在沿线的位置反而在起始点之前(即用户给出的 stopPt 在线上更靠前),函数会通过比较segmentIndex自动交换两端顺序,保证截取结果始终是从线头部向尾部方向的连续子段,不会产生"反向或空结果"。
4. 组装截取坐标并返回
const clipCoords = [ends[0].geometry.coordinates]; for ( let i = ends[0].properties.segmentIndex + 1; i < ends[1].properties.segmentIndex + 1; i++ ) { clipCoords.push(coords[i]); } clipCoords.push(ends[1].geometry.coordinates); return linestring(clipCoords, line.type === "Feature" ? line.properties : {});截取结果的坐标组成为:起始投影点 → 途经的完整中间顶点 → 终止投影点。即两个投影端点之间原本落在线上的中间顶点会全部保留,折线的形状细节不会丢失。最后调用@turf/helpers的linestring构造函数生成新的线要素,并通过line.type === "Feature"判断来决定是否继承原线的properties(Feature 输入则保留属性,Geometry 输入则返回空属性)。
边界情况与已知问题的处理
segmentIndex 越界修正(Issue #3016)
源码中专门定义了一个内部函数fixSegmentIndexBounds(index.ts):
function fixSegmentIndexBounds(line, vertex) { let geometry = line.type === "Feature" ? line.geometry : line; if (vertex.properties.segmentIndex >= geometry.coordinates.length - 1) { vertex.properties.segmentIndex = geometry.coordinates.length - 2; } }在 index.ts 中,startVertex和stopVertex在排序前都会经过该函数修正。源码注释说明了原因:nearestPointOnLine存在一个已知 bug,可能返回超出实际范围的segmentIndex(指向不存在的线段),在 Turf 官方修复(对应 issue 编号 #3016)之前,lineSlice在此处主动将越界索引钳制到length - 2,保证后续遍历坐标数组时不会越界。
重复端点问题(Issue #2946)
在 test.ts 中有专门针对 issue #2946 的回归测试:输入线[[2.999848, 0.000152], [2, -1], [2, 2]],起点[2, 0]、终点[2, 2],断言截取结果只有两个点(coordinates.length === 2),即不会因为投影算法引入重复点;同时断言对截取结果再次调用lineSlice不会抛错。该用例的 fixture 也保存在 test/in/avoid-duplicated-end-points.geojson 中。
非 LineString 输入
如前所述,传入非 LineString 几何会抛出类型错误。这是函数最直接的使用约束:lineSlice 只支持折线数据,如果需要处理其他几何类型,请先通过@turf/line-to-polygon、@turf/polygon-to-line等模块做转换。
测试用例与验证
lineSlice的测试集中在 test.ts,采用 tape 断言 + GeoJSON fixture 的方式:
Fixture 驱动测试(test.ts):遍历 test/in/ 目录下的 6 个输入 fixture(
line1、line2、route1、route2、vertical、avoid-duplicated-end-points),每个 fixture 按[linestring, start, stop]顺序解析要素,调用lineSlice后先用@turf/truncate统一截断坐标精度(默认 6 位小数),再与 test/out/ 中的期望结果做深度比对。覆盖的典型场景:
line1展示一般折线截取(line1.geojson);line2中起止点均偏离线段([0, 0.1]与[0.9, 0.8]均不在对角线[0,0]→[1,1]上),验证投影能力(line2.geojson);vertical验证竖直方向、起点恰为线端点的场景(vertical.geojson);route1/route2则是上千个顶点的真实路线数据,验证大规模折线的截取正确性。Issue 回归测试:除 #2946 外,还有针对 issue #2023 的测试(test.ts),使用长距离直线
[[3.69140625, 51.72702815704774], [-5.3173828125, 41.60722821271717]],断言截取结果精确到 8 位小数的坐标值。
运行测试的命令在 package.json 中定义:
pnpm run test:tape # 等价于 tsx test.ts性能基准
仓库提供了 bench.ts 基准脚本,基于benchmark库对三种规模的数据进行压测:
turf-line-slice#simple:使用 3 个顶点的简单折线(line1);turf-line-slice#route1与turf-line-slice#route2:使用真实大路线(上千顶点),验证大规模折线下的截取效率。
运行方式:
pnpm run bench # 等价于 tsx bench.ts从算法实现看,lineSlice的性能开销主要取决于nearestPointOnLine的逐段最近点搜索,实际耗时与线段的顶点数量线性相关,路线越长顶点越多,计算成本越高。
典型应用场景
- 路线区间展示:地图上一条完整路线可能横跨数百公里,只需渲染用户起终点间的部分时,先用
lineSlice截取再绘图,减少前端渲染负担; - 轨迹分段分析:将 GPS 轨迹按事件点(停留点、兴趣点)切成多段,逐段计算长度、耗时或速度;
- 路径裁剪预处理:将截取结果继续输入 @turf/length 计算区间里程,或配合
@turf/truncate控制输出坐标精度(测试中也正是这样组合使用的)。
小结
@turf/line-slice用简洁的 API 完成了"两点截线"这一高频地理计算需求:三个参数、一个返回值,内部却串联了类型校验、最近点投影、索引排序与坐标重组四步逻辑,并针对上游nearestPointOnLine的索引越界 bug 做了主动钳制。配合完善的 fixture 测试与基准脚本,无论用于路线、轨迹还是路径处理,它都是一个可靠且易于理解的模块。更多组合用法可参考 packages/turf 聚合入口,或阅读相邻模块 @turf/nearest-point-on-line 了解投影算法的细节。
- 数据分析
【免费下载链接】turf
A modular geospatial engine written in JavaScript and TypeScript
相关推荐
@turf/line-slice-along 实战指南:按里程提取折线子路段
@turf/line slice along 实战指南:按里程提取折线子路段 本指南围绕 Turf 模块化地理引擎(JavaScript/TypeScript
数据分析@turf/line-offset 指南:用 Turf 为线要素生成平行偏移线
@turf/line offset 指南:用 Turf 为线要素生成平行偏移线 @turf/line offset 是 Turf 模块化地理引擎中专门用于"线偏
数据分析使用 @turf/line-split 切割线要素:Turf 中 LineString 分段切割的完整指南
使用 @turf/line split 切割线要素:Turf 中 LineString 分段切割的完整指南 本指南围绕 Turf 模块集合中的 @turf/li
数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考