Turf.js 怎么实现点是否在多边形内的判断
本文详细讲解使用 Turf.js 实现点是否在多边形内判断的完整方案,解析 booleanPointInPolygon 的使用方法、GeoJSON 坐标规范、边界与孔洞处理及避坑指南。
在物流配送划区、外卖运力派单、共享单车禁停区管制或电子围栏告警等场景中,系统经常需要实时判断某个用户、车辆或设备经纬度坐标是否位于指定多边形服务范围内。在前端或 Node.js 环境下,Turf.js 提供了标准方法 booleanPointInPolygon,接收点与多边形的 GeoJSON 对象作为参数,直接返回代表是否在多边形内部的布尔值(true 或 false)。
本文将围绕 booleanPointInPolygon 展开完整讲解,从环境准备、标准数据格式构造、核心代码调用,到边界判定配置与多边形孔洞等进阶情况,帮助读者快速掌握点在多边形内判定的标准实现与排查方法。
1. 核心方法与模块引入
在早期 Turf.js 版本(v4 及更早)中,判断点是否在多边形内的函数曾被称为 turf.inside。从 v5.0 开始,官方重构了空间谓词体系,turf.inside 已被全面废弃,统一规范为 booleanPointInPolygon。该函数基于计算几何中的经典射线法(Ray Casting Algorithm)实现,既支持单点与简单多边形,也兼容带孔多边形和复合多边形(MultiPolygon)。
根据项目架构,可以通过以下两种方式引入该功能:
方式 A:npm / yarn 模块化安装(推荐)
现代工程化项目既可以按需引入独立包以减小体积,也可以引入完整的 Turf 工具库:
# 推荐:按需安装独立子模块(体积轻量,仅约 10KB)
npm install @turf/boolean-point-in-polygon @turf/helpers
# 或安装完整工具库
npm install @turf/turf
代码中通过 ES Module 语法导入:
// 方式一:独立包导入
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import { point, polygon } from '@turf/helpers';
// 方式二:全量包导入
import * as turf from '@turf/turf';
// 此时调用 turf.booleanPointInPolygon(pt, poly)
方式 B:浏览器 CDN 脚本直接引入
如果是在静态页面或轻量可视化 Demo 中快速验证,可以直接引入官方 CDN 脚本:
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script>
// 全局变量 turf 直接可用
console.log(typeof turf.booleanPointInPolygon); // "function"
</script>
2. 必须遵循的 GeoJSON 数据结构
许多开发者在初次使用 Turf.js 时报错或得到意外的 false,绝大多数并非逻辑代码问题,而是传入的几何坐标格式不符合 GeoJSON 规范。在调用 booleanPointInPolygon 前,必须确认以下两项核心规则:
规则 1:坐标顺序始终是 [经度, 纬度],切勿颠倒
国内地图 API(如高德地图、百度地图、腾讯地图)以及部分移动端 SDK 在传参时常习惯使用 [纬度, 经度] 或 { lat, lng } 对象。然而 GeoJSON 国际标准 (RFC 7946) 与数学笛卡尔坐标系一致,X 轴对应经度 (Longitude),Y 轴对应纬度 (Latitude)。因此,传入的点坐标必须是 [lng, lat] 数组。
规则 2:多边形首尾点必须相同,闭合为环(LinearRing)
GeoJSON 的 Polygon 坐标由嵌套数组组成。最外层的线环表示多边形的外边界,其首个坐标点与最后一个坐标点必须完全一致,否则就属于非法几何体。例如一个三角形多边形,必须包含至少 4 个坐标点(首尾相接)。
3. 实现点在多边形内判定的具体步骤
下面给出一个完整可运行的代码示例,包含测试点构造、多边形电子围栏定义及调用判断:
import * as turf from '@turf/turf';
// 第一步:定义待判定的点(例如设备的实时 GPS 定位,经度在前,纬度在后)
// 北京天安门广场附近点位
const targetPoint = turf.point([116.3974, 39.9092]);
// 第二步:定义多边形区域(例如一个配送责任区,注意首尾点必须闭合)
const deliveryZone = turf.polygon([[
[116.3800, 39.9000],
[116.4200, 39.9000],
[116.4200, 39.9300],
[116.3800, 39.9300],
[116.3800, 39.9000] // 闭合点,与首点相同
]]);
// 第三步:调用 booleanPointInPolygon 进行空间包含判定
const isInside = turf.booleanPointInPolygon(targetPoint, deliveryZone);
// 第四步:输出执行结果与后续业务分支
console.log('目标点是否在配送区域内:', isInside);
if (isInside) {
console.log('进入电子围栏:正常接单与派送');
} else {
console.log('超出电子围栏:触发超区告警或限制服务');
}
示例执行输出(示例展示):
目标点是否在配送区域内: true
进入电子围栏:正常接单与派送
除了传入 Turf 包装过的 Feature 对象外,该函数还具备极佳的参数容错性:第一个参数可以直接传入裸坐标数组 [116.3974, 39.9092],第二个参数也可以直接传入原生 GeoJSON 的 geometry 几何体,无需必须显式调用 turf.point() 包装。
4. 进阶场景:边界点、孔洞与复合多边形
在实际业务中,点位刚好落在多边形轮廓线上、多边形内部带有禁行“孔洞”(湖泊、军事禁区等),或配送区域由多个分散板块组成(MultiPolygon),需要了解以下关键机制:
4.1 边界点判定选项(options.ignoreBoundary)
booleanPointInPolygon 支持第三个可选参数 options。其中最重要的参数是 ignoreBoundary:
ignoreBoundary: false(默认值):当点刚好落在多边形的边界线(轮廓)上时,依然视作在多边形内部,返回true。ignoreBoundary: true:当点位于边界线上时,不视为在内部,返回false。
// 位于多边形顶点的临界点
const boundaryPoint = turf.point([116.3800, 39.9000]);
// 默认包含边界 -> true
console.log(turf.booleanPointInPolygon(boundaryPoint, deliveryZone));
// 忽略边界(边界点不计入) -> false
console.log(turf.booleanPointInPolygon(boundaryPoint, deliveryZone, { ignoreBoundary: true }));
4.2 带孔多边形(Polygon with Holes)
GeoJSON 规范中,polygon(coordinates) 的 coordinates 数组可以包含多个环:第一个环表示外边界,后续每一个环都表示内部被掏空的孔洞(内环)。如果点落在外环内、但同时落入内环内,booleanPointInPolygon 会自动判定为不在多边形内(返回 false):
// 外环 + 内环孔洞
const outerRing = [
[116.30, 39.90], [116.40, 39.90], [116.40, 40.00], [116.30, 40.00], [116.30, 39.90]
];
const innerHole = [
[116.33, 39.93], [116.37, 39.93], [116.37, 39.97], [116.33, 39.97], [116.33, 39.93]
];
const polygonWithHole = turf.polygon([outerRing, innerHole]);
// 点位于孔洞内部
const pointInHole = turf.point([116.35, 39.95]);
console.log(turf.booleanPointInPolygon(pointInHole, polygonWithHole)); // 输出: false
4.3 复合多边形(MultiPolygon)
如果业务区域是由多个不连通的岛状板块组成(例如跨江行政区、离岛),直接使用 turf.multiPolygon() 构造,booleanPointInPolygon 原生支持 MultiPolygon,只要点落在任意一个子多边形内,即返回 true。
5. 核心注意事项与常见问题解答
1. 坐标系不一致导致的“肉眼在内、代码报 false”
在中国的地理信息系统中,存在 WGS-84(GPS 原始坐标)、GCJ-02(高德、腾讯等使用的国测局加密坐标)和 BD-09(百度加密坐标)。三者在同一地点上的经纬度数值可能相差数百米。如果多边形是从高德地图导出的(GCJ-02),而设备 GPS 定位使用的是原始 WGS-84,判定结果极易发生越界误报。解决办法是必须在进入 Turf.js 计算前,统一转换至同一坐标系。
2. 大批量点位与大范围多边形的性能优化
如果需要在前端或服务端同时对几万个点进行实时围栏过滤,逐个对复杂多边形执行 booleanPointInPolygon 会带来显著的 CPU 消耗。推荐的工业级优化策略如下:
- 外包矩形预筛选(BBox Filter):先使用
turf.bbox(polygon)计算多边形的最小外接矩形[minX, minY, maxX, maxY]。对每个点,只需一次极轻量的区间判断point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY。只有落入外接矩形的点,才进一步调用booleanPointInPolygon做精确几何相交计算。该策略通常能过滤掉 90% 以上的无效点。 - 空间索引(R-Tree / Flatbush / rbush):若存在成百上千个多边形围栏,先利用 R-Tree 建立空间索引,快速圈定候选多边形,再执行精细判定。
6. 总结与速查清单
| 检查维度 | 规范要求 | 常见错误表现 |
|---|---|---|
| 核心方法 | turf.booleanPointInPolygon(point, polygon, [options]) |
使用已弃用的 turf.inside 导致运行时报错 |
| 坐标顺序 | [经度 Longitude, 纬度 Latitude] |
按 [lat, lng] 传入导致点偏移到极地或公海 |
| 环闭合 | 多边形外环首尾坐标必须完全一致 (首点 == 尾点) | 多边形少传闭合点,导致几何体拓扑不合法 |
| 边界点处理 | 配置 { ignoreBoundary: true/false } |
压线点由于未明确边界配置导致结果与业务预期不符 |
| 坐标系统 | 参与计算的点与面必须处于同一坐标基准 (WGS-84 / GCJ-02) | 混用不同厂商坐标导致数百米人为空间偏移 |
掌握以上规范和 booleanPointInPolygon 的调用细节后,无论是构建轻量级的 Web 电子围栏监控,还是在 Node.js 后端服务中进行批量运力划区判定,都能高效、稳定地实现空间几何位置的准确核验。
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
为什么JS中typeof []返回object:数组类型检测误区解析
2026-11-04 19:50:47








