【php】地址圍欄,判斷地圖某一個點是否在地址圍欄內(百度地圖示例)


地址圍欄效果圖:

 

1、controller代碼:

/**
    * 判斷一個點是否在某個區域內。百度,高德,騰訊都能用。(php版)
    * @param $lng+$lat ==>> 經緯度;$area_arr ==>> 三維數組
    * @return return === false : 點不在區域內;
    *         return 0, 1, 2, 3 ... 點所在的區域編號
    * @author xzz 2019年5月14日 下午3:34:27
    */
    public function addressIn($lng='114.236559',$lat='30.531549'){
        $area_arr = [];
        if(cache('address_area_arr')){
            $area_arr = cache('address_area_arr');
        }else{
            $_area_id = [];
            $_area_id = Db::name('address_area')->where('delete_time is null')->column('id');
            foreach($_area_id as $v){
                $site_arr = [];
                $site_arr = Db::name('address_site')
                        ->field('lng x,lat y')
                        ->where('delete_time is null')
                        ->where('status=1')
                        ->where('address_area_id',$v)
                        ->select()
                        ->toArray();
                $area_arr[] = $site_arr;
            }
            cache('address_area_arr',$area_arr);
        }
        import('Area',EXTEND_PATH);
        $area = new \Area($area_arr);
        

        var_dump($area->checkPoint($lng,$lat));
    }

2、area類,放在extend/ 

<?php
/*
    *** 配置文件(表示區域的三維數組)其內的點,必須按順時針方向依次給出!
    *** 確定一點是否在一區域(多邊形)內:
        1:過這一點(x0, y0),畫一水平線(y=y0),與多邊形的所有邊進行交點判斷。
        2:獲取交點集(其中不含多邊形的頂點)
        3:若該點(x0, y0)的左側和右側交點個數均為奇數個,則該點在區域(多邊形)內。否則:不在。
    *** 返回結果:
        return === false : 點不在區域內
        return 0, 1, 2, 3 ... 點所在的區域編號(配置文件中的區域編號。)
    *** Author : Guojunzhou / Eric
    *** Main : php20141104@163.com    
*/
class Area{
    // 一個表示區域的三維數組
    protected $config = null;
 
    // 包含每個區域的四邊形
    protected $rectangles = null;
 
    // 每個區域(多邊形)的所有邊
    protected $lines = null;
 
    // 要判斷的點的x, y坐標
    protected $_x = null;
    protected $_y = null;
 
    public function __construct($config){
        $this->config = $config;
        $this->initRectangles();
        $this->initLines();
    }
 
    /*
        獲取包含每個配送區域的四邊形
    */
    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坐標最小值
    */
    private function getMinXInEachConfig($index){
        $minX = 200;
        foreach ($this->config[$index] as $k => $v) {
            if($v['x'] < $minX){
                $minX = $v['x'];
            }
        }
        return $minX;
    }
 
    /*
        獲取一組坐標中,y坐標最小值
    */
    private function getMinYInEachConfig($index){
        $minY = 200;
        foreach ($this->config[$index] as $k => $v) {
            if($v['y'] < $minY){
                $minY = $v['y'];
            }
        }
        return $minY;
    }
 
    /*
        獲取一組坐標中,x坐標最大值
    */
    public function getMaxXInEachConfig($index){
        $maxX = 0;
        foreach ($this->config[$index] as $k => $v) {
            if($v['x'] > $maxX){
                $maxX = $v['x'];
            }
        }
        return $maxX;
    }
 
    /*
        獲取一組坐標中,y坐標最大值
    */
    public function getMaxYInEachConfig($index){
        $maxY = 0;
        foreach ($this->config[$index] as $k => $v) {
            if($v['y'] > $maxY){
                $maxY = $v['y'];
            }
        }
        return $maxY;
    }
 
    /*
        獲取 y=y0 與特定區域的所有邊的交點,並去除和頂點重復的,再將交點分為左和右兩部分
    */    
    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 ... 點所在的區域編號(配置文件中的區域編號。)
    */
    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;
                break;
            }
        }
        if($contain === null) return false;
        $crossPoint = $this->getCrossPointInCertainConfig($contain);
        if($crossPoint === true) return $contain;
        if(count($crossPoint['left'])%2 == 1 && count($crossPoint['right'])%2 == 1) return $contain;
        return false;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM