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; } }