news 2026/9/25 3:30:16

Turf rectangleGrid 矩形网格生成完全指南:从 API 参数到源码级实现原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Turf rectangleGrid 矩形网格生成完全指南:从 API 参数到源码级实现原理
  • 数据分析

【免费下载链接】turf

A modular geospatial engine written in JavaScript and TypeScript

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

导读

@turf/rectangle-grid是 Turf 模块化地理空间引擎(项目仓库)中的网格生成模块,用于在指定的地理包围盒(bbox)内生成一组宽度、高度在角度(degrees)维度上保持一致的矩形多边形网格。本文以 packages/turf-rectangle-grid/README.md 为骨架,结合模块源码 index.ts、单元测试 test.ts 与基准测试 bench.ts,系统讲解rectangleGrid的完整 API、单位换算原理、居中算法、掩膜裁剪以及测试与性能表现,帮助你掌握在 Turf 生态中快速生成规则矩形网格的实战能力。

一、模块概览与安装

@turf/rectangle-grid是 Turf 众多模块中专门负责"规则矩形网格"生成的模块。从 package.json 可以看到,它的描述是 "Creates a grid of rectangular polygons with width and height consistent in degrees",关键词为grid、regular、cartesian,当前版本为7.4.0,采用 ESM 模块规范("type": "module"),运行环境要求 Node.js >= 22。

安装有两种方式:

单独安装本模块:

$ npm install @turf/rectangle-grid

或安装包含全部模块的聚合包@turf/turf,这样所有模块都会以函数形式挂载到turf对象上:

$ npm install @turf/turf

从 package.json 的dependencies可以看出,本模块底层依赖三个 Turf 兄弟模块:@turf/boolean-intersects(用于掩膜相交判定)、@turf/distance与@turf/helpers(提供convertLength、featureCollection、polygon等工具函数),这为后续源码分析提供了线索。

二、rectangleGrid API 详解

rectangleGrid函数接受一个 bbox、单元格宽度、单元格高度以及可选参数,返回一个FeatureCollection<Polygon>类型的多边形网格。

2.1 参数表

参数类型说明
bboxBBox网格范围,按[minX, minY, maxX, maxY]顺序给出;若网格无法完美填满 bbox,结果会居中
cellWidthnumber每个单元格的宽度,单位由options.units指定
cellHeightnumber每个单元格的高度,单位由options.units指定
optionsObject可选参数,默认{}
options.unitsUnits单元格宽高的单位,支持 Turf 全部合法单位,默认'kilometers'
options.maskFeature<Polygon\|MultiPolygon>可选;传入 Polygon 或 MultiPolygon 时,只在掩膜内部生成网格多边形
options.propertiesObject传递给网格中每个多边形的属性,默认{}

返回值:FeatureCollection<Polygon>——一个包含若干矩形多边形的要素集合。

2.2 官方示例

原 README 给出的最小可运行示例(以英里为单位生成矩形网格):

var bbox = [-95, 30 ,-85, 40]; var cellWidth = 50; var cellHeight = 20; var options = {units: 'miles'}; var rectangleGrid = turf.rectangleGrid(bbox, cellWidth, cellHeight, options); //addToMap var addToMap = [rectangleGrid]

在 ESM / TypeScript 环境下,也可以直接导入模块化函数:

import rectangleGrid from "@turf/rectangle-grid"; const bbox = [-95, 30, -85, 40]; const grid = rectangleGrid(bbox, 50, 20, { units: "miles" });

2.3 options.units:支持的单位与"角度一致"的语义

options.units支持 Turf 定义的全部合法单位。完整的单位清单定义在 packages/turf-helpers/README_UNITS.md,包括:

  • meters/metres
  • millimeters/millimetres
  • centimeters/centimetres
  • kilometers/kilometres
  • miles
  • nauticalmiles
  • inches
  • yards
  • feet
  • radians
  • degrees

这里有一个关键语义需要特别注意(README 与源码注释都反复强调):如果你需要的是在线性单位(如千米)下宽度与高度相等的正方形网格,这个模块并不适合你。因为cellWidth与cellHeight会在内部从给定单位换算为度(degrees),所以最终生成的多边形,其宽高只在"度"这一角度度量上保持一致,而非在地球表面真实的线性距离上保持一致。这一点是使用rectangleGrid时最容易踩的坑。

三、源码级实现原理

3.1 调用链与依赖关系

从 index.ts 的导入语句可见,函数内部依赖@turf/helpers的convertLength、featureCollection、polygon以及@turf/boolean-intersects的booleanIntersects。核心流程为:单位换算 → 行列数计算 → 居中偏移 → 双循环生成多边形 → 掩膜过滤 → 打包成 FeatureCollection。

3.2 单位换算:convertLength

rectangleGrid将宽高统一换算为度:

const cellWidthDeg = convertLength(cellWidth, options.units, "degrees"); const cellHeightDeg = convertLength(cellHeight, options.units, "degrees");

convertLength定义在 packages/turf-helpers/index.ts,其实现为:

export function convertLength( length: number, originalUnit: Units = "kilometers", finalUnit: Units = "kilometers" ): number { if (!(length >= 0)) { throw new Error("length must be a positive number"); } return radiansToLength(lengthToRadians(length, originalUnit), finalUnit); }

它先将长度转为弧度,再转为目标单位,并且会校验length必须为非负数。这意味着传入负的cellWidth/cellHeight会直接抛出"length must be a positive number"错误。

3.3 行列计算与居中算法

const bboxWidth = east - west; const bboxHeight = north - south; const columns = Math.floor(Math.abs(bboxWidth) / cellWidthDeg); const rows = Math.floor(Math.abs(bboxHeight) / cellHeightDeg); // 若网格无法完美填满 bbox,将其居中 const deltaX = (bboxWidth - columns * cellWidthDeg) / 2; const deltaY = (bboxHeight - rows * cellHeightDeg) / 2; let currentX = west + deltaX; for (let column = 0; column < columns; column++) { let currentY = south + deltaY; for (let row = 0; row < rows; row++) { // 构造 [西,南,东,北] 对应的五坐标闭合环 const cellPoly = polygon( [ [ [currentX, currentY], [currentX, currentY + cellHeightDeg], [currentX + cellWidthDeg, currentY + cellHeightDeg], [currentX + cellWidthDeg, currentY], [currentX, currentY], ], ], options.properties ); ... currentY += cellHeightDeg; } currentX += cellWidthDeg; }

可以看到实现的关键点:

  1. 行列数向下取整:Math.floor保证了生成的网格不会超出 bbox 边界;
  2. 居中策略:当bboxWidth % cellWidthDeg !== 0时,剩余宽度的一半被作为deltaX偏移量加到起始 X 上,deltaY同理,使网格整体在 bbox 内居中,而不是从 bbox 的西南角平铺;这正是 README 中"If the grid does not fill the bbox perfectly, it is centered"的源码实现;
  3. 单元格为闭合五坐标环:每个矩形由西南角起、按逆时针方向依次经过西北、东北、东南、再回到西南角共 5 个坐标构成闭合环(首尾坐标相同),符合 GeoJSON Polygon 的线性环闭合要求;
  4. 属性透传:options.properties会作为第二个参数传入polygon(),因此每个生成的单元格都会携带相同的自定义属性。

3.4 掩膜(mask)过滤机制

当传入options.mask时,源码使用booleanIntersects做相交测试,只有与掩膜相交的单元格才被保留:

if (options.mask) { if (intersect(options.mask, cellPoly)) { results.push(cellPoly); } } else { results.push(cellPoly); }

需要注意:这里采用的是**相交(intersects)而非包含(contains)**判定,即只要单元格与掩膜多边形有任意重叠就会被保留。因此当掩膜是复杂的不规则多边形(例如澳大利亚边界)时,边界处的单元格往往会被部分截断成不完整矩形,输出结果是"被掩膜裁剪过的网格集合"。

最后,所有保留的单元格通过featureCollection(results)打包返回。

四、实战案例:来自测试夹具的完整用法

模块的 test 目录 提供了 6 组真实测试夹具,覆盖了不同单位、不同 bbox 大小与掩膜场景,是理解 API 用法的第一手资料:

夹具文件bboxcellWidthcellHeightunits说明
10x10-1degree.json局部范围1010degrees以度为单位的等宽高网格
victoria-20x100-km.json[141, -39, 150, -34]20100kilometers千米单位、宽高不对称网格
fiji-10x5-miles.json斐济范围105miles英里单位网格
big-bbox-500x100-miles.json[-220.78125, -80.647, -29.53125, 78.349]500100miles跨越大范围的大 bbox
global-grid.json[-180, -90, 180, 90]1010degrees全球范围网格
australia-mask.json[110, 0, 160, -50]12degrees带澳大利亚多边形掩膜

例如使用维多利亚州范围的千米单位网格:

{ "bbox": [141, -39, 150, -34], "cellWidth": 20, "cellHeight": 100, "units": "kilometers" }

调用方式即:

const grid = rectangleGrid([141, -39, 150, -34], 20, 100, { units: "kilometers" });

再如带掩膜的用例(掩膜为澳大利亚大陆的 Polygon,1×2 度单元格):

{ "bbox": [110, 0, 160, -50], "cellWidth": 1, "cellHeight": 2, "units": "degrees", "mask": { "type": "Feature", "properties": {}, "geometry": { "type": "Polygon", "coordinates": [...] } } }

调用方式:

const grid = rectangleGrid([110, 0, 160, -50], 1, 2, { units: "degrees", mask: maskFeature, // 澳大利亚多边形 });

4.1 如何运行测试

测试脚本定义在 package.json 中:

pnpm test:tape # 等价于 tsx test.ts

test.ts 会读取test/in下每个夹具,以夹具 JSON 中的bbox、cellWidth、cellHeight、units、properties、mask字段调用rectangleGrid,再用@turf/truncate截断坐标精度后与test/out目录中的期望输出做深度相等断言;同时会把 bbox(红色)与掩膜(蓝色)作为样式要素追加进结果,便于可视化核对。若设置环境变量REGEN,则会将实际结果写回test/out重新生成期望文件。

4.2 性能表现

bench.ts 内置了三个量级的基准测试(均在[-95, 30, -85, 40]的 bbox 上):

场景单元格宽高单元格数量吞吐量
highres1 × 2 英里约 206310 个约 5.99 ops/sec
midres10 × 20 英里约 2006 个约 3388 ops/sec
lowres100 × 200 英里15 个约 466370 ops/sec

运行基准:

pnpm bench # 等价于 tsx bench.ts

该结果(注释中记录的实测值)直观地表明:性能随单元格数量线性下降,单元格数量是决定耗时的最主要因素,因此在大范围、高分辨率网格场景下应评估数据规模与渲染/存储成本。

五、使用建议与注意事项

  1. 明确"度"的语义:rectangleGrid的单元格宽高最终以度为单位一致,生成的矩形在赤道附近接近真实等距,在高纬度地区线性距离会被拉长。如需线性单位下真正等距的正方形网格,应改用@turf/square-grid或@turf/point-grid等按目标投影计算的方案。
  2. bbox 顺序不可颠倒:bbox必须严格按[minX, minY, maxX, maxY]传入;源码直接以west = bbox[0]、south = bbox[1]、east = bbox[2]、north = bbox[3]解构,顺序错误会导致网格方向与预期不符。
  3. 宽高为非负数:convertLength会对负数抛出"length must be a positive number"异常,请保证cellWidth、cellHeight为正数。
  4. 掩膜是相交判定:options.mask按booleanIntersects过滤,掩膜边界处的单元格会被保留但呈不完整形态,若要精确裁剪单元格本身,需对结果另行做@turf/intersect处理。
  5. 属性透传:options.properties会原样附加到每个单元格多边形上,可用于为网格附加业务字段(如索引编号、区域分类),无需事后二次遍历赋值。

六、小结

rectangleGrid是一个设计简洁、语义清晰的网格生成工具:三个必选参数加上units、mask、properties三个可选参数,即可在任意 bbox 内生成角度一致的矩形网格。其源码(index.ts)以"单位换算 → 取整分行列 → 居中偏移 → 双循环构造闭合环 → 掩膜相交过滤 → 打包 FeatureCollection"为主线,配合 test.ts 的多组真实夹具与 bench.ts 的性能基准,构成了从 API 到实现的完整闭环。在需要快速生成规则格网用于空间索引、热力分区、采样布点等场景时,本模块是最直接的选择;而需要线性单位等距网格时,请选择 Turf 的其他网格模块。

  • 数据分析

【免费下载链接】turf

A modular geospatial engine written in JavaScript and TypeScript

项目地址:https://gitcode.com/gh_mirrors/tu/turf
点击查看免费下载
上一篇:超实用Thief-Book-Plugin插件:从安装到精通的零门槛指南
下一篇:终极adr-tools环境变量配置指南:高级用户定制化全解析

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:30:15

ACM模式Java输入输出全攻略:从Scanner到快读模板

刷题刷到一定阶段&#xff0c;你就会发现一个绕不开的坎&#xff1a;ACM模式。这个词在Java面试题和算法题库里反复出现&#xff0c;很多在IDE里写惯了LeetCode式核心代码的朋友&#xff0c;第一次在笔试系统里碰见要自己处理输入输出的题目时&#xff0c;当场就懵了。键盘倒是…

作者头像 李华
网站建设 2026/9/25 3:30:02

AI记忆系统设计实战:从会话上下文到跨会话长效记忆

1. 从“AI 失忆”说起&#xff1a;为什么记忆是智能的最短木板做过 NLP、跑过对话系统、搭过智能客服的朋友&#xff0c;大概率都遇到过同一个尴尬场景&#xff1a;模型上一轮还能准确回答“我叫小明&#xff0c;今年 28 岁”&#xff0c;下一轮换个句式问“我多大了”&#xf…

作者头像 李华
网站建设 2026/9/25 3:29:21

谢希仁计算机网络课件:可运行、可验证、可调试的教学活体切片

简介&#xff1a;本资源是谢希仁《计算机网络》第6版&#xff08;“十二五”国家级规划教材&#xff09;配套的完整课件PPT&#xff0c;面向高校电气信息类、计算机类本科生及研究生&#xff0c;也适用于网络工程技术人员系统复习核心理论与协议体系。课件共1173页&#xff0c;…

作者头像 李华
网站建设 2026/9/25 3:27:29

成都好吃美食门店口碑哪家好?华商广场行业现状与正规商家选择指南

成都好吃美食餐馆哪个值得去?成都好吃美食餐厅口碑哪家好?成都好吃美食店家哪个好?这三个问题是成都本地食客、来蓉商务人群和旅游游客搜索最多的三个问题&#xff0c;接下来我们逐一解答。Q1&#xff1a;成都好吃美食门店口碑哪家好?说到成都好吃的美食&#xff0c;很多人…

作者头像 李华