判断坐标点是否在坐标围起来的区域内

场景

有个需求需要判断车辆 gps 上传的坐标点是否在所管辖的区域内

代码

  • 判断一个坐标是否在一个多边形内(由多个坐标围成的)

  • 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则

  • 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。

  • @param $point 指定点坐标 ["lon" => "104.24398627", "lat" => "30.29790868"]

  • @param $pts 多边形坐标 顺时针方向
    [["lon" => 104.271928,"lat" => 30.494732],
    ["lon" => 104.280496,"lat" => 30.493443]]

function is_point_in_polygon($point, $pts)
{
    $N = count($pts);
    $boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
    $intersectCount = 0;//cross points count of x
    $precision = 2e-10; //浮点类型计算时候与0比较时候的容差
    $p1 = 0;//neighbour bound vertices
    $p2 = 0;
    $p = $point; //测试点
    $p1 = $pts[0];//left vertex
    for ($i = 1; $i <= $N; ++$i) {//check all rays
        if ($p['lon'] == $p1['lon'] && $p['lat'] == $p1['lat']) {

            return $boundOrVertex;//p is an vertex

        }

        $p2 = $pts[$i % $N];//right vertex

        if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) {//ray is outside of our interests

            $p1 = $p2;
            continue;//next ray left point

        }if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) {//ray is crossing over by the algorithm (common part of)

            if($p['lon'] <= max($p1['lon'], $p2['lon'])){//x is before of ray

                if ($p1['lat'] == $p2['lat'] && $p['lon'] >= min($p1['lon'], $p2['lon'])) {//overlies on a horizontal ray

                    return $boundOrVertex;

                }if ($p1['lon'] == $p2['lon']) {//ray is vertical

                    if ($p1['lon'] == $p['lon']) {//overlies on a vertical ray

                        return $boundOrVertex;

                    }else {//before ray

                        ++$intersectCount;

                    }

                }else {//cross point on the left side

                    $xinters = ($p['lat'] - $p1['lat']) * ($p2['lon'] - $p1['lon']) / ($p2['lat'] - $p1['lat']) + $p1['lon'];//cross point of lon

                    if (abs($p['lon'] - $xinters) < $precision) {//overlies on a ray

                        return $boundOrVertex;

                    }if ($p['lon'] < $xinters) {//before ray

                        ++$intersectCount;

                    }

                }

            }

        }else {//special case when ray is crossing through the vertex

            if ($p['lat'] == $p2['lat'] && $p['lon'] <= $p2['lon']) {//p crossing over p2

                $p3 = $pts[($i+1) % $N]; //next vertex

                if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) { //p.lat lies between p1.lat & p3.lat

                    ++$intersectCount;

                }else{

                    $intersectCount += 2;

                }
            }
        }
        $p1 = $p2;//next ray left point
    }
    if ($intersectCount % 2 == 0) {//偶数在多边形外
        return false;
    }else { //奇数在多边形内
        return true;
    }
}
posted @ 2022-09-18 17:19  __、  阅读(668)  评论(0编辑  收藏  举报