☰
@turf/line-slice 实战指南:用 Turf 精准截取线要素子段
2026/9/25 16:10:03 网站建设 项目流程
  • 数据分析

【免费下载链接】turf

A modular geospatial engine written in JavaScript and TypeScript

项目地址:https://gitcode.com/gh_mirrors/tu/turf
点击查看免费下载

导读

@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> // 返回截取后的线
参数类型说明
startPtCoord起始点,可以是PointFeature、PointGeometry 或坐标数组
stopPtCoord终止点,类型同上
lineFeature<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

项目地址:https://gitcode.com/gh_mirrors/tu/turf
点击查看免费下载
上一篇:游戏对话一行看不懂?用 Translumo 实时屏幕翻译的 4 种打开方式
下一篇:marked 中 Tab 字符的解析全解:缩进代码块、列表嵌套与制表位展开原理

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询