123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- <?php
- namespace App\Handlers;
- class CheckAreaHandler
- {
- // 一个表示区域的三维数组
- 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]['longitude'] - $this->config[$k][$i + 1]['longitude'] == 0) $this->lines[$k][$i]['k'] = 0;
- else $this->lines[$k][$i]['k'] =
- ($this->config[$k][$i]['latitude'] - $this->config[$k][$i + 1]['latitude']) / ($this->config[$k][$i]['longitude'] - $this->config[$k][$i + 1]['longitude']);
- // y=kx+b : b
- $this->lines[$k][$i]['b'] = $this->config[$k][$i + 1]['latitude'] - $this->lines[$k][$i]['k'] * $this->config[$k][$i + 1]['longitude'];
- $this->lines[$k][$i]['lx'] = min($this->config[$k][$i]['longitude'], $this->config[$k][$i + 1]['longitude']);
- $this->lines[$k][$i]['rx'] = max($this->config[$k][$i]['longitude'], $this->config[$k][$i + 1]['longitude']);
- }
- $pointNum -= 1;
- if ($this->config[$k][$pointNum]['longitude'] - $this->config[$k][0]['longitude'] == 0) $this->lines[$k][$pointNum]['k'] = 0;
- else $this->lines[$k][$pointNum]['k'] =
- ($this->config[$k][$pointNum]['latitude'] - $this->config[$k][0]['latitude']) / ($this->config[$k][$pointNum]['longitude'] - $this->config[$k][0]['longitude']);
- // y=kx+b : b
- $this->lines[$k][$pointNum]['b'] = $this->config[$k][0]['latitude'] - $this->lines[$k][$pointNum]['k'] * $this->config[$k][0]['longitude'];
- $this->lines[$k][$pointNum]['lx'] = min($this->config[$k][$pointNum]['longitude'], $this->config[$k][0]['longitude']);
- $this->lines[$k][$pointNum]['rx'] = max($this->config[$k][$pointNum]['longitude'], $this->config[$k][0]['longitude']);
- }
- }
- /*
- 获取一组坐标中,x坐标最小值
- */
- private function getMinXInEachConfig($index)
- {
- $minX = 200;
- foreach ($this->config[$index] as $k => $v) {
- if ($v['longitude'] < $minX) {
- $minX = $v['longitude'];
- }
- }
- return $minX;
- }
- /*
- 获取一组坐标中,y坐标最小值
- */
- private function getMinYInEachConfig($index)
- {
- $minY = 200;
- foreach ($this->config[$index] as $k => $v) {
- if ($v['latitude'] < $minY) {
- $minY = $v['latitude'];
- }
- }
- return $minY;
- }
- /*
- 获取一组坐标中,x坐标最大值
- */
- public function getMaxXInEachConfig($index)
- {
- $maxX = 0;
- foreach ($this->config[$index] as $k => $v) {
- if ($v['longitude'] > $maxX) {
- $maxX = $v['longitude'];
- }
- }
- return $maxX;
- }
- /*
- 获取一组坐标中,y坐标最大值
- */
- public function getMaxYInEachConfig($index)
- {
- $maxY = 0;
- foreach ($this->config[$index] as $k => $v) {
- if ($v['latitude'] > $maxY) {
- $maxY = $v['latitude'];
- }
- }
- return $maxY;
- }
- /*
- 获取 y=y0 与特定区域的所有边的交点,并去除和顶点重复的,再将交点分为左和右两部分
- */
- private function getCrossPointInCertainConfig($index)
- {
- $crossPoint = ['left' => [], 'right' => []];
- 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($lng, $lat)
- {
- $this->_x = $lng;
- $this->_y = $lat;
- $contain = null;
- foreach ($this->rectangles as $k => $v) {
- if ($lng > $v['maxX'] || $lng < $v['minX'] || $lat > $v['maxY'] || $lat < $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;
- }
- }
|