判断坐标是否在地图围栏中,自测可用

//********************************判断坐标是否在地图围栏中begin*************************
    // 一个表示区域的三维数组
    protected $config = null;

    // 包含每个区域的四边形
    protected $rectangles = null;

    // 每个区域(多边形)的所有边
    protected $lines = null;

    // 要判断的点的x, y坐标
    protected $_x = null;
    protected $_y = null;


    /**
     * 获取包含每个配送区域的四边形
     */
    private function initRectangles(){
        foreach ($this->config as $k => $v) {
            $this->rectangles[$k]['minX'] = $this->getMinXInEachConfig($k);
            $this->rectangles[$k]['minY'] = $this->getMinYInEachConfig($k);
            $this->rectangles[$k]['maxX'] = $this->getMaxXInEachConfig($k);
            $this->rectangles[$k]['maxY'] = $this->getMaxYInEachConfig($k);
        }
    }

    /**
     * 初始化每个区域(多边形)的边(线段:直线的一部分【限制x或者y坐标范围】)
     * n 个顶点构成的多边形,有 n-1 条边
     */
    private function initLines(){
        foreach ($this->config as $k => $v) {
            $pointNum = count($v);        // 区域的顶点个数
            $lineNum = $pointNum - 1;     // 区域的边条数
            for($i=0; $i<$lineNum; $i++){
                // y=kx+b : k
                if($this->config[$k][$i]['x'] - $this->config[$k][$i+1]['x'] == 0) $this->lines[$k][$i]['k'] = 0;
                else $this->lines[$k][$i]['k'] =
                    ($this->config[$k][$i]['y'] - $this->config[$k][$i+1]['y'])/($this->config[$k][$i]['x'] - $this->config[$k][$i+1]['x']);
                // y=kx+b : b
                $this->lines[$k][$i]['b'] = $this->config[$k][$i+1]['y'] - $this->lines[$k][$i]['k'] * $this->config[$k][$i+1]['x'];
                $this->lines[$k][$i]['lx'] = min($this->config[$k][$i]['x'], $this->config[$k][$i+1]['x']);
                $this->lines[$k][$i]['rx'] = max($this->config[$k][$i]['x'], $this->config[$k][$i+1]['x']);
            }
            $pointNum-=1;
            if($this->config[$k][$pointNum]['x'] - $this->config[$k][0]['x'] == 0) $this->lines[$k][$pointNum]['k'] = 0;
            else $this->lines[$k][$pointNum]['k'] =
                ($this->config[$k][$pointNum]['y'] - $this->config[$k][0]['y'])/($this->config[$k][$pointNum]['x'] - $this->config[$k][0]['x']);
            // y=kx+b : b
            $this->lines[$k][$pointNum]['b'] = $this->config[$k][0]['y'] - $this->lines[$k][$pointNum]['k'] * $this->config[$k][0]['x'];
            $this->lines[$k][$pointNum]['lx'] = min($this->config[$k][$pointNum]['x'], $this->config[$k][0]['x']);
            $this->lines[$k][$pointNum]['rx'] = max($this->config[$k][$pointNum]['x'], $this->config[$k][0]['x']);
        }
    }

    /**
     * 获取一组坐标中,x坐标最小值
     * @param $index
     * @return int
     */
    private function getMinXInEachConfig($index){
        $minX = 200;
        foreach ($this->config[$index] as $k => $v) {
            if($v['x'] < $minX){
                $minX = $v['x'];
            }
        }
        return $minX;
    }

    /**
     * 获取一组坐标中,y坐标最小值
     * @param $index
     * @return int
     */
    private function getMinYInEachConfig($index){
        $minY = 200;
        foreach ($this->config[$index] as $k => $v) {
            if($v['y'] < $minY){
                $minY = $v['y'];
            }
        }
        return $minY;
    }

    /**
     * 获取一组坐标中,x坐标最大值
     * @param $index
     * @return int
     */
    public function getMaxXInEachConfig($index){
        $maxX = 0;
        foreach ($this->config[$index] as $k => $v) {
            if($v['x'] > $maxX){
                $maxX = $v['x'];
            }
        }
        return $maxX;
    }

    /**
     * 获取一组坐标中,y坐标最大值
     * @param $index
     * @return int
     */
    public function getMaxYInEachConfig($index){
        $maxY = 0;
        foreach ($this->config[$index] as $k => $v) {
            if($v['y'] > $maxY){
                $maxY = $v['y'];
            }
        }
        return $maxY;
    }

    /**
     * 获取 y=y0 与特定区域的所有边的交点,并去除和顶点重复的,再将交点分为左和右两部分
     * @param $index
     * @return bool|null
     */
    private function getCrossPointInCertainConfig($index){
        $crossPoint = null;
        foreach ($this->lines[$index] as $k => $v) {
            if($v['k'] == 0) return true;
            $x0 = ($this->_y - $v['b']) / $v['k'];    // 交点x坐标
            if($x0 == $this->_x) return true;        // 点在边上
            if($x0 > $v['lx'] && $x0 < $v['rx']){
                if($x0 < $this->_x) $crossPoint['left'][] = $x0;
                if($x0 > $this->_x) $crossPoint['right'][] = $x0;
            }
        }
        return $crossPoint;
    }

    /**
     * 检测一个点,是否在区域内
     * 返回结果:
     * return === false : 点不在区域内
     * return 0, 1, 2, 3 ... 点所在的区域编号(配置文件中的区域编号。)
     * @param $x
     * @param $y
     * @return array
     */
    public function checkPoint($x, $y){
        $this->_x = $x;
        $this->_y = $y;
        $contain = null;
        foreach ($this->rectangles as $k => $v) {
            if($x > $v['maxX'] || $x < $v['minX'] || $y > $v['maxY'] || $y < $v['minY']){
                continue;
            }else{
                $contain[] = $k;
            }
        }
        $ids = [];
        if($contain === null) return $ids;
        foreach($contain as $contain_one) {
            $crossPoint = $this->getCrossPointInCertainConfig($contain_one);

            if($crossPoint === true) {
                $ids[] = $contain_one;
                continue;
            }

            $left = empty($crossPoint['left']) ? 0 : count($crossPoint['left']);
            $right = empty($crossPoint['right']) ? 0 : count($crossPoint['right']);
            if($left%2 == 1 && $right%2 == 1) $ids[] = $contain_one;
        }
        return $ids;
    }


    /**
     * 判断一个坐标是否在一个多边形内(由多个坐标围成的)
     * 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
     * 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
     * @param $point 指定点坐标
     * @param $pts 多边形坐标 顺时针方向
     * @return bool
     */
    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
            // dump($p1);
            if ($p['x'] == $p1['x'] && $p['y'] == $p1['y']) {
                return $boundOrVertex;//p is an vertex
            }

            $p2 = $pts[$i % $N];//right vertex
            if ($p['y'] < min($p1['y'], $p2['y']) || $p['y'] > max($p1['y'], $p2['y'])) {//ray is outside of our interests
                $p1 = $p2;
                continue;//next ray left point
            }

            if ($p['y'] > min($p1['y'], $p2['y']) && $p['y'] < max($p1['y'], $p2['y'])) {//ray is crossing over by the algorithm (common part of)
                if($p['x'] <= max($p1['x'], $p2['x'])){//x is before of ray
                    if ($p1['y'] == $p2['y'] && $p['x'] >= min($p1['x'], $p2['x'])) {//overlies on a horizontal ray
                        return $boundOrVertex;
                    }

                    if ($p1['x'] == $p2['x']) {//ray is vertical
                        if ($p1['x'] == $p['x']) {//overlies on a vertical ray
                            return $boundOrVertex;
                        } else {//before ray
                            ++$intersectCount;
                        }
                    } else {//cross point on the left side
                        $xinters = ($p['y'] - $p1['y']) * ($p2['x'] - $p1['x']) / ($p2['y'] - $p1['y']) + $p1['x'];//cross point of x
                        if (abs($p['x'] - $xinters) < $precision) {//overlies on a ray
                            return $boundOrVertex;
                        }

                        if ($p['x'] < $xinters) {//before ray
                            ++$intersectCount;
                        }
                    }
                }
            } else {//special case when ray is crossing through the vertex
                if ($p['y'] == $p2['y'] && $p['x'] <= $p2['x']) {//p crossing over p2
                    $p3 = $pts[($i+1) % $N]; //next vertex
                    if ($p['y'] >= min($p1['y'], $p3['y']) && $p['y'] <= max($p1['y'], $p3['y'])) { //p.y lies between p1.y & p3.y
                        ++$intersectCount;
                    } else {
                        $intersectCount += 2;
                    }
                }
            }
            $p1 = $p2;//next ray left point
        }

        if ($intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }
    //********************************判断坐标是否在地图围栏中end*************************
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值