//********************************判断坐标是否在地图围栏中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*************************
06-10
1429
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
10-19
5064
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)