- 数据分析
【免费下载链接】turf
A modular geospatial engine written in JavaScript and TypeScript
三角网格(Triangular Grid)是空间分析中常用的规则网格形态,常用于采样、插值、表面建模与专题制图。本文以 Turf.js 模块化地理引擎中的@turf/triangle-grid模块为对象,完整讲解其 API 参数、安装方式、底层实现原理与测试验证方法,帮助你基于给定的地理范围(bbox)与单元格边长,在数行代码内生成一张覆盖目标区域的三角多边形网格。
模块定位与安装
@turf/triangle-grid是 Turf 系列中负责"在指定矩形范围内生成三角网格"的独立子模块(当前仓库中版本为7.4.0,见 package.json)。它以 GeoJSONFeatureCollection<Polygon>形式返回结果,输入输出均遵循 RFC 7946 标准,可直接被地图库或后端空间分析管线消费。
官方 README(即本文所依据的 README.md)提供两种安装方式:
方式一:单独安装本模块
$ npm install @turf/triangle-grid方式二:安装聚合模块 @turf/turf
$ npm install @turf/turf第二种方式会把所有 Turf 模块作为函数一次性纳入,适合在整体引入 Turf 的场景中使用;第一种方式则适合按需加载、控制包体大小的项目。仓库基于 pnpm workspace 管理,模块声明为"type": "module",且要求 Node.js>= 22(见 package.json)。
API 签名与参数详解
triangleGrid的完整函数签名如下(引自 index.ts):
function triangleGrid<P extends GeoJsonProperties = GeoJsonProperties>( bbox: BBox, cellSide: number, options: { units?: Units; properties?: P; mask?: Feature<Polygon>; } = {} ): FeatureCollection<Polygon, P>各参数的含义与要点如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
bbox | BBox | 是 | 网格覆盖范围,按[minX, minY, maxX, maxY]顺序给出,即经度最小值、纬度最小值、经度最大值、纬度最大值 |
cellSide | number | 是 | 每个网格单元的边长(在该单位下),每个单元格内会生成两个三角形 |
options.units | Units | 否 | 计算cellSide时使用的单位,支持 Turf 的全部合法单位(如kilometers、miles、degrees等),默认值为'kilometers' |
options.mask | Feature<Polygon> | 否 | 若传入 Polygon(或 MultiPolygon),网格三角形只保留落在该范围内部的部分 |
options.properties | Object | 否 | 透传给网格中每个要素的属性对象,默认值为{} |
返回值:FeatureCollection<Polygon>,即由三角形多边形组成的要素集合。
关于单位的完整枚举与换算规则,可参见 README_UNITS.md(位于@turf/helpers模块内)。
快速上手示例
README 给出的最小可用示例(README.md):
var bbox = [-95, 30 ,-85, 40]; var cellSide = 50; var options = {units: 'miles'}; var triangleGrid = turf.triangleGrid(bbox, cellSide, options); //addToMap var addToMap = [triangleGrid];该示例在经度-95至-85、纬度30至40的矩形范围内,以50 英里为单元边长生成三角网格。若使用 ES Module 方式按模块导入,则可写作:
import { triangleGrid } from "@turf/triangle-grid"; const bbox = [-95, 30, -85, 40]; const cellSide = 50; const options = { units: "miles" }; const grid = triangleGrid(bbox, cellSide, options); // grid.features 中即为三角形多边形集合把生成的FeatureCollection交给任意 GeoJSON 渲染器即可看到三角形铺满 bbox 的效果。需要说明的是:网格单元是依据cellSide在经纬度空间内等分 bbox 得到的矩形,再按对角线一分为二成两个三角形,因此覆盖区域仍严格限制在 bbox 矩形内。
底层实现原理:从 cellSide 到三角网
阅读 index.ts 的源码,可以完整还原该模块的算法流程,共分三步:
1. 将单位制下的边长换算为经纬度增量
由于cellSide以公里、英里等距离单位给出,而 bbox 以经纬度表示,源码借助@turf/distance完成换算(index.ts):
var xFraction = cellSide / distance([bbox[0], bbox[1]], [bbox[2], bbox[1]], options); var cellWidth = xFraction * (bbox[2] - bbox[0]); var yFraction = cellSide / distance([bbox[0], bbox[1]], [bbox[0], bbox[3]], options); var cellHeight = yFraction * (bbox[3] - bbox[1]);这里分别测量了 bbox底边(两个点经度不同、纬度相同)和左边(两个点纬度不同、经度相同)的球面距离,把cellSide与整条边的长度求比,得到"边长占 bbox 的分数",再乘回经纬度跨度,从而把距离单位换算为适合当前经纬度的cellWidth与cellHeight。这意味着同一cellSide在不同纬度地区对应的经纬度步长会自动不同——越靠近高纬度,经度方向的实际距离被压缩,换算出的cellWidth会相应放大,从而在物理距离上保持一致。
2. 双循环铺满 bbox
随后源码以currentX/currentY为游标、xi/yi为行列计数,用嵌套while循环从bbox[0], bbox[1]出发,依次以cellWidth、cellHeight为步长扫描到 bbox 右上角(index.ts)。每个单元格生成两个三角形,且依据xi与yi的奇偶性切换四种朝向组合(index.ts):
xi偶、yi偶:左下与右上两个三角形;xi偶、yi奇:另一种对角分割;xi奇、yi偶:再一种对角分割;xi奇、yi奇:与第一种相反的斜向分割。
这种棋盘式交替(checkerboard)保证了相邻单元格的三角形边能够对齐拼接,形成无缝的三角网格,这也是"每个 cell 生成两个三角形"这一设计的目的所在。
3. 输出与掩膜裁剪
所有三角形收集到results数组后,最终通过featureCollection(results)组装为FeatureCollection<Polygon>返回(index.ts)。
使用 mask 做不规则范围裁剪
当业务只需要 bbox 内某一不规则区域的网格时,传入options.mask即可。源码中的处理逻辑(index.ts)是:对每个候选三角形与 mask 构造一个临时FeatureCollection,调用@turf/intersect求交;若存在交集则保留该三角形,否则丢弃:
if (options.mask) { if (intersect(featureCollection([options.mask, cellTriangle1!]))) results.push(cellTriangle1!); if (intersect(featureCollection([options.mask, cellTriangle2!]))) results.push(cellTriangle2!); } else { results.push(cellTriangle1!); results.push(cellTriangle2!); }因此options.mask需为 Polygon 或 MultiPolygon 类型的 Feature。仓库测试夹具 piedemont-mask.json 就演示了在意大利皮埃蒙特地区 bbox 内、以 12.5 英里为边长、并给定一块不规则多边形掩膜(代表真实行政区/区域轮廓)的完整配置:
{ "bbox": [6.3775634765625, 44.05601169578525, 9.437255859375, 46.50595444552049], "cellSide": 12.5, "units": "miles", "mask": { "type": "Feature", "properties": { "stroke": "#00F", "stroke-width": 6, "fill-opacity": 0.3, "fill": "#00F" }, "geometry": { "type": "Polygon", "coordinates": [ /* ...顶点序列... */ ] } } }需要留意的是:mask 裁剪采用"逐三角形求交"的方式,属于逐要素判定,当 bbox 很大或cellSide很小时,三角形数量激增,求交开销会相应上升,此时应合理权衡网格分辨率。
properties 透传与 TypeScript 泛型支持
options.properties会被原样附加到每个生成的三角形要素上。源码中每个polygon(...)调用的第二个参数都是options.properties(例如 index.ts)。这在需要为网格要素标注类型、分组或渲染样式时非常实用,测试夹具 properties.json 即演示了:
{ "bbox": [2.131519317626953, 41.37835427979543, 2.2264480590820312, 41.44388449101261], "properties": { "marker-color": "#0ff" }, "cellSide": 1 }此外,该模块对 TypeScript 支持良好:triangleGrid<P>是泛型函数,P即 properties 的类型参数。仓库中的类型测试 types.ts 验证了如下用法:
const grid = triangleGrid(bbox, 50, { units: "miles", properties: { foo: "bar" }, }); grid.features[0].properties.foo; // 类型安全,可正常访问 // grid.features[0].properties.bar // 类型错误:'bar' 不存在于 '{ 'foo': string; }'即传入的properties类型会被精确推导并约束到返回的FeatureCollection<Polygon, P>上,为编译期发现属性名错误提供了保障。
测试用例与性能基准
仓库为每个 Turf 模块配套了测试与基准,便于验证与评估:
- 功能测试:test.ts 基于
tape编写,遍历 test/in 下的全部夹具(读取其中的bbox、cellSide、units、mask、properties字段),调用triangleGrid生成结果,用@turf/truncate截断坐标精度后,与 test/out 目录中的预期 GeoJSON 逐要素deepEqual比对;设置环境变量REGEN可重新生成预期文件。 - 测试夹具覆盖:包括斐济群岛 10 英里网格(fiji-10-miles.json)、伦敦 20 英里网格(london-20-miles.json)、跨越东西经度的超大 bbox(500 英里,big-bbox.json)、高纬度的 Resolute 地区 12.5 英里网格(resolute.json)、带掩膜与带 properties 的组合场景,覆盖了不同纬度、不同单位与不同选项的典型用法。
- 性能基准:bench.ts 使用
benchmark库,在同一 bbox 上分别以 100、10、1 英里为边长生成网格,统计对应生成的三角形数量(grid(...).features.length)并逐个跑分,便于观察分辨率与生成耗时的关系。
在仓库中可这样运行:
# 运行功能测试(在模块目录内) pnpm --filter @turf/triangle-grid test # 运行性能基准 pnpm --filter @turf/triangle-grid bench注意事项与边界
- 单位默认值:未传
options.units时按'kilometers'处理,跨项目复用代码时建议显式声明单位,避免语义歧义。 - 运行环境:当前版本要求 Node.js
>= 22且使用 ESM("type": "module",见 package.json),在旧版本 Node 或 CommonJS 项目中需先确认兼容性。 - 网格范围:网格严格铺满整个 bbox 矩形;若需不规则范围,请使用
mask,但要注意逐三角形求交的性能开销。 - 模块依赖:本模块内部依赖
@turf/distance、@turf/helpers与@turf/intersect(见 package.json),单独安装时这些依赖会自动随包安装。
小结
@turf/triangle-grid以"bbox + cellSide + 可选掩膜"三个核心输入,即可输出标准 GeoJSON 三角网格,适合快速为空间分析搭建规则采样框架。结合本文对 index.ts 源码(距离换算、双循环铺格、棋盘式朝向、mask 求交)与测试夹具的分析,你可以准确预估不同参数下的网格形态与开销,并将其无缝接入 Turf 的网格、插值、统计与可视化工作流。
- 数据分析
【免费下载链接】turf
A modular geospatial engine written in JavaScript and TypeScript
相关推荐
使用 @turf/tin 构建不规则三角网(TIN):从散点生成多边形三角剖分
使用 @turf/tin 构建不规则三角网(TIN):从散点生成多边形三角剖分 @turf/tin 是 Turf 空间分析模块集合中负责 三角剖分 的核心模块:
数据分析Wagtail 2.16.2 版本解析:关键 Bug 修复全梳理与 Jinja2 升级注意事项
Wagtail 2.16.2 版本解析:关键 Bug 修复全梳理与 Jinja2 升级注意事项 docs/releases/2.16.2.md 记录了 Wagt
数据分析Bourbon三角形生成器:triangle mixin使用指南
Bourbon三角形生成器:triangle mixin使用指南 你是否还在为CSS三角形代码编写繁琐而烦恼?是否经常忘记border属性组合的方向规则?Bou
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考