当前位置: 首页 > java >正文

turf的pointsWithinPolygon排查

进击的WebGIS(1)。

在WebGIS开发过程中经常使用到turf,最近遇到了一个奇怪的问题,使用pointsWithinPolygon用一个面去过滤点集,有几个点是明显分布在面中的,但最近的结果是一个空的FeatureCollection。

在使用 Turf.js 的 pointsWithinPolygon 时返回空结果,通常有以下几类原因。请逐步排查:


1. 检查数据格式是否正确

  • 点和多边形必须是 GeoJSON Feature 或 FeatureCollection

    Turf 要求输入为标准的 GeoJSON 结构,例如:

    // 点数据格式示例
    const point = {type: 'Feature',geometry: {type: 'Point',coordinates: [经度, 纬度] // 注意顺序是 [lng, lat]}
    };// 多边形数据格式示例(必须闭合)
    const polygon = {type: 'Feature',geometry: {type: 'Polygon',coordinates: [[[lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1]]] // 首尾坐标相同}
    };
    
  • 错误示例:直接传递几何对象(如 geometry: {...})而非完整的 Feature 对象。


2. 确认坐标顺序 [经度, 纬度]

  • GeoJSON 要求坐标顺序为 [经度(lng), 纬度(lat)],而非 [纬度, 经度]。
  • 如果顺序颠倒,点可能位于完全不同的位置(例如非洲附近海域),导致结果为 0。

3. 验证多边形是否闭合

  • 多边形的坐标数组必须首尾闭合(即第一个点和最后一个点相同)。

  • 错误示例:

    // 未闭合的多边形(缺少最后一个点)
    [[[0,0], [1,0], [1,1], [0,1]]] // ❌ 错误
    
  • 正确示例

    [[[0,0], [1,0], [1,1], [0,1], [0,0]]] // ✅ 正确
    

4. 检查点是否实际在多边形内

  • 使用 turf.booleanPointInPolygon手动验证单个点:(这一点非常有效且必要,因为源码中pointsWithinPolygon就是遍历使用了booleanPointInPolygon方法,booleanPointInPolygon方法的算法其实是射线法)。

    const point = turf.point([lng, lat]);
    const isInside = turf.booleanPointInPolygon(point, polygon);
    

console.log(isInside); // true/false


- **注意**:Turf 默认不包含多边形边界上的点(可通过 `{ ignoreBoundary: false }` 调整)。------### 5. **确认参数顺序**- ```
pointsWithinPolygon的第一个参数是 点集合,第二个是 多边形
// ✅ 正确用法
const pointsInside = turf.pointsWithinPolygon(points, polygon);// ❌ 错误用法(参数颠倒)
const pointsInside = turf.pointsWithinPolygon(polygon, points);

6. 检查坐标系一致性

  • 确保所有数据使用相同的坐标系(例如 WGS84)。
  • 如果数据来自其他来源(如平面坐标系),需先转换为经纬度。

7. 其他可能原因

  • Turf 版本问题:升级到最新版本(npm update @turf/turf)。
  • 数据范围不重叠:确保点和多边形在地理范围内有交集(可先用 turf.bbox 检查边界框)。

8. 常规马虎不易查错误

  • 拼写错误:方法名拼写错误、字母顺序错误、其他字符拼写错误。
  • 大小写问题:FeatureCollection中{type:“Feature”}中Feature是要大写的,若用feature,尤其在一些早期版本,极容易发生空几何的错误,其实是源码中的大小写判断兼容问题(仅判断Feature)。

快速调试示例

const turf = require('@turf/turf');// 示例多边形(闭合的矩形)
const polygon = turf.polygon([[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0] // 首尾闭合
]]);// 示例点(一个在内部,一个在外部)
const points = turf.featureCollection([turf.point([5, 5]),  // 内部turf.point([15, 15]) // 外部
]);// 执行查询
const result = turf.pointsWithinPolygon(points, polygon);
console.log(result.features.length); // 应返回 1points.features.forEach(point=>{let pointInside = turf.booleanPointInPolygon(point, polygon);if(pointInside) console.log("点在面内");
})

如果仍有问题,欢迎留言。

以下是一个完整的、不依赖任何第三方库的 booleanPointInPolygon 实现代码,基于射线法(Ray Casting Algorithm),支持 多边形带孔洞边界条件处理

/*** 判断点是否在多边形内(射线法)* @param {number[]} point - 待测点坐标 [x, y]* @param {number[][][]} polygon - 多边形坐标(支持带孔),格式为:*   [*     [[x1,y1], [x2,y2], ..., [x1,y1]], // 外环*     [[x1,y1], [x2,y2], ..., [x1,y1]]  // 内环(孔洞,可选)*   ]* @param {boolean} [ignoreBoundary=true] - 是否忽略边界(点在边上不算内部)* @return {boolean}*/
function booleanPointInPolygon(point, polygon, ignoreBoundary = true) {const [x, y] = point;let inside = false;const epsilon = 1e-6; // 浮点计算容差// 遍历所有环(外环 + 内环)for (const ring of polygon) {for (let i = 0; i < ring.length - 1; i++) {const v1 = ring[i];const v2 = ring[i + 1];const [x1, y1] = v1;const [x2, y2] = v2;// 检查点是否在顶点上if (isEqual(point, v1, epsilon) || isEqual(point, v2, epsilon)) {return !ignoreBoundary;}// 检查点是否在水平边上if (Math.abs(y1 - y2) < epsilon && Math.abs(y - y1) < epsilon) {const minX = Math.min(x1, x2);const maxX = Math.max(x1, x2);if (x >= minX && x <= maxX) return !ignoreBoundary;}// 检查点是否在边所在的直线上,并且在线段内if (isOnSegment(point, v1, v2, epsilon)) {return !ignoreBoundary;}// 射线与边相交判断if ((y1 > y) !== (y2 > y)) {const xIntersect = ((y - y1) * (x2 - x1)) / (y2 - y1) + x1;// 处理浮点误差if (Math.abs(x - xIntersect) < epsilon) {return !ignoreBoundary;}if (x < xIntersect) {inside = !inside;}}}}return inside;
}// 判断两个点是否相等(考虑浮点误差)
function isEqual(a, b, epsilon) {return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
}// 判断点是否在线段上
function isOnSegment(p, a, b, epsilon) {const [x, y] = p;const [x1, y1] = a;const [x2, y2] = b;// 先判断是否共线const cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);if (Math.abs(cross) > epsilon) return false;// 再判断是否在矩形范围内const minX = Math.min(x1, x2) - epsilon;const maxX = Math.max(x1, x2) + epsilon;const minY = Math.min(y1, y2) - epsilon;const maxY = Math.max(y1, y2) + epsilon;return x >= minX && x <= maxX && y >= minY && y <= maxY;
}

使用示例

// 定义一个带孔的正方形(外环+内环)
const polygon = [[[0,0], [10,0], [10,10], [0,10], [0,0]],  // 外环[[2,2], [8,2], [8,8], [2,8], [2,2]]       // 内环(孔洞)
];console.log(booleanPointInPolygon([5,5], polygon));    // true(在外环内,内环外)
console.log(booleanPointInPolygon([5,5], polygon, false)); // false(严格内部)console.log(booleanPointInPolygon([1,1], polygon));    // true
console.log(booleanPointInPolygon([5,5], polygon));    // false(在内环内)console.log(booleanPointInPolygon([5,0], polygon));    // false(在边上,ignoreBoundary=true)
console.log(booleanPointInPolygon([5,0], polygon, false)); // true(在边上,ignoreBoundary=false)

关键特性

  1. 支持复杂多边形
    • 处理带孔洞的多边形(多个环)
    • 自动处理内外环的奇偶规则
  2. 边界条件处理
    • 点在边上(精确判断线段是否包含点)
    • 点在顶点上
    • 水平边特殊情况
  3. 浮点误差容差
    • 使用 epsilon 避免精度问题
  4. 灵活边界控制
    • 通过 ignoreBoundary 参数控制是否包含边界

注意事项

  • 多边形必须闭合:每个环的最后一个点必须与第一个点相同。

  • 坐标系方向:假设为笛卡尔坐标系(X 轴向右,Y 轴向上),如需经纬度坐标(Y 轴向下),需反转 Y 轴比较逻辑。

  • 输入坐标应为 [[[x,y], ...]] 格式(与 GeoJSON 一致)。

    • 水平边特殊情况
  1. 浮点误差容差
    • 使用 epsilon 避免精度问题
  2. 灵活边界控制
    • 通过 ignoreBoundary 参数控制是否包含边界

注意事项

  • 多边形必须闭合:每个环的最后一个点必须与第一个点相同。

  • 坐标系方向:假设为笛卡尔坐标系(X 轴向右,Y 轴向上),如需经纬度坐标(Y 轴向下),需反转 Y 轴比较逻辑。

  • 输入坐标应为 [[[x,y], ...]] 格式(与 GeoJSON 一致)。

在这里插入图片描述

http://www.xdnf.cn/news/7391.html

相关文章:

  • window xampp apache使用腾讯云ssl证书配置https
  • 算法(最小基因变化+迷宫中离入口最近的出口)
  • C# 枚举 详解
  • linux kernel 编译
  • java的arraylist集合
  • TransactionSynchronizationManager事务同步器的使用
  • 统计客户端使用情况,使用es存储数据,实现去重以及计数
  • 【全解析】EN18031标准下的SCM安全通信机制全解析
  • 质检LIMS系统检测数据可视化大屏 全流程提效 + 合规安全双保障方案
  • 视频监控中的存储方式有哪些?EasyCVR视频监控汇聚平台如何打造高效监控存储
  • 高速系统设计实例设计分析之三
  • 蓝桥杯2300 质数拆分
  • 码蹄集——N是什么、棋盘
  • JVM(Java 虚拟机)深度解析
  • web基础常用标签
  • More Effective C++:改善编程与设计(下)
  • Seata源码—6.Seata AT模式的数据源代理三
  • 洛谷U536262 井底之“鸡” 附视频讲解
  • 提示词专家的修炼秘籍
  • harris角点检测
  • VisionPro:轴承错位标识
  • QT之绘图模块和双缓冲技术
  • MapStruct Date 转 LocalDate 偏差一天问题
  • 【C++】异常解析
  • AGI大模型(28):LangChain提示模板
  • MySQL中的Change Buffer是什么,它有什么作用?
  • 火山 RTC 引擎9 ----集成 appkey
  • 5月19日笔记
  • 4.7 时间模块
  • 网络传输(ping命令,wget命令,curl命令),端口