点在多边形(Point in Polygon)算法处理负坐标返回错误结果求助
跨国际日期变更线的点-in-多边形问题解决办法
嘿,你碰到的这个问题其实是跨国际日期变更线的经纬度处理坑,不是算法不支持正负坐标哦!你的多边形顶点同时包含东半球(正经度,比如170.x)和西半球(负经度,比如-178.x),而原来的射线法算法默认坐标是连续的平面坐标系,完全没考虑经度的环绕特性(经度范围是-180到180,越过180/-180其实是连续的),所以才会判断错误。
问题出在哪?
你的多边形跨越了180°经线,此时多边形的一条边会从东半球的170直接跳到西半球的-178——在平面坐标系里这看起来是一条横跨整个地图的超长线段,但在球面上其实是很短的一段弧线。原来的射线法会错误计算这条边和射线的交点,自然就得出了错误的“不在多边形内”的结论。
另外还要提一句:你原来传的$points_polygon = count($vertices_x) - 1是错的!函数循环里$i < $points_polygon,如果传count-1,会少循环一次,这也是潜在的bug。
怎么解决?
最直接的办法是坐标偏移法,把跨变更线的多边形转换成平面上正常的多边形,再用射线法判断:
- 先判断多边形是否跨日期变更线:如果顶点的最大经度和最小经度差超过180,说明跨线了
- 把所有西半球的经度(负数值)加上360,转换成正值(比如-178变成182),目标点的经度也做同样处理
- 用偏移后的坐标跑原来的射线法就可以了
修改后的完整PHP函数
function is_in_polygon($vertices_x, $vertices_y, $longitude_x, $latitude_y) { $point_count = count($vertices_x); // 先做参数合法性检查 if ($point_count !== count($vertices_y)) { throw new InvalidArgumentException("顶点的经度数组和纬度数组长度必须一致"); } if ($point_count < 3) { return false; // 至少3个顶点才能构成多边形 } // 处理跨国际日期变更线的情况 $crosses_dateline = false; $max_lon = max($vertices_x); $min_lon = min($vertices_x); // 经度范围超过180,说明跨了变更线 if ($max_lon - $min_lon > 180) { $crosses_dateline = true; // 把所有负经度转成正的(+360),统一到一个平面坐标系里 $vertices_x = array_map(function($lon) { return $lon < 0 ? $lon + 360 : $lon; }, $vertices_x); $longitude_x = $longitude_x < 0 ? $longitude_x + 360 : $longitude_x; } $inside = false; // 射线法核心逻辑,这里修正了循环的起始值 for ($i = 0, $j = $point_count - 1; $i < $point_count; $j = $i++) { $yi = $vertices_y[$i]; $yj = $vertices_y[$j]; $xi = $vertices_x[$i]; $xj = $vertices_x[$j]; // 判断射线是否穿过当前边 if ((($yi > $latitude_y) != ($yj > $latitude_y)) && ($longitude_x < ($xj - $xi) * ($latitude_y - $yi) / ($yj - $yi) + $xi)) { $inside = !$inside; } } return $inside; }
用你的测试数据调用
// 你的原始参数 $vertices_x = [-32.581189, -38.785885, -39.26384, -34.919383, -32.284464]; $vertices_y = [170.643905, 170.424179, -178.15004, -176.524063, -178.325821]; $target_lon = 173.5385; $target_lat = -34.472; // 调用函数 var_dump(is_in_polygon($vertices_x, $vertices_y, $target_lon, $target_lat)); // 现在应该返回bool(true),也就是你预期的“在多边形内”
额外说明
- 我把函数参数里的
$points_polygon去掉了,直接从数组长度获取,避免了你之前的传参错误 - 加了参数合法性检查,防止出现数组长度不一致或者顶点数不够的情况
- 坐标偏移的逻辑把跨变更线的多边形转换成了平面上正常的形状,射线法就能正确计算交点了
内容的提问来源于stack exchange,提问作者user3997016
相关产品推荐
相关产品推荐

