地址圍欄效果圖:
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; } }