php实现 计算坐标点在某区域
项目需求:通过地图坐标和区域坐标点集合 判断当前坐标是否在坐标点集合区域中
$area[array] 区域坐标点集合数组
$area = array( // 天通苑店 0 => array( array('x'=>116.38295, 'y'=>40.09416), array('x'=>116.44037, 'y'=>40.095898), array('x'=>116.448275,'y'=>40.083313), array('x'=>116.448455,'y'=>40.050818), array('x'=>116.448275,'y'=>40.038307), array('x'=>116.441448,'y'=>40.038418), array('x'=>116.436058,'y'=>40.038804), array('x'=>116.417302,'y'=>40.039136), array('x'=>116.414822,'y'=>40.039384), array('x'=>116.412738,'y'=>40.039329), array('x'=>116.407672,'y'=>40.039329), array('x'=>116.388628,'y'=>40.085162), array('x'=>116.383633,'y'=>40.084997) ), //亚运村 1 => array( array('x'=>116.358804,'y'=>40.028474), array('x'=>116.41608, 'y'=>40.02875), array('x'=>116.41723, 'y'=>40.038915), array('x'=>116.447988,'y'=>40.037921), array('x'=>116.447844,'y'=>40.026761), array('x'=>116.455821,'y'=>40.024164), array('x'=>116.446281,'y'=>39.994736), array('x'=>116.443532,'y'=>39.995372), array('x'=>116.376267,'y'=>39.993493), array('x'=>116.375908,'y'=>40.000015), array('x'=>116.372027,'y'=>39.999904), array('x'=>116.371452,'y'=>40.007366), array('x'=>116.359451,'y'=>40.006758) ), //望京店 2 => array( array('x'=>116.46387, 'y'=>40.021125), array('x'=>116.484495,'y'=>40.020462), array('x'=>116.515684,'y'=>39.995151), array('x'=>116.51519, 'y'=>39.976137), array('x'=>116.491906,'y'=>39.972985), array('x'=>116.476239,'y'=>39.977298), array('x'=>116.467472,'y'=>39.96917), array('x'=>116.443325,'y'=>39.984817), array('x'=>116.449506,'y'=>39.993109), array('x'=>116.446357,'y'=>39.994736), array('x'=>116.456037,'y'=>40.024109) ), );
<?php
/**
* 验证坐标点是否在某区域内
* @author xiaoliang <1058436713@qq.com>
* Class validationMap
*/
class InareaAction extends CommonAction {
// 一个表示区域的三维数组
public $config = null;
// 包含每个区域的四边形
public $rectangles = null;
// 每个区域(多边形)的所有边
public $lines = null;
// 要判断的点的x, y坐标
public $_x = null;
public $_y = null;
public function __construct($config){
$this->config = $config;
$this->initRectangles();
$this->initLines();
}
/*
获取包含每个配送区域的四边形
*/
public 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 条边
*/
public 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坐标最小值
*/
public function getMinXInEachConfig($index){
$minX = 200;
foreach ($this->config[$index] as $k => $v) {
if($v['x'] < $minX){
$minX = $v['x'];
}
}
return $minX;
}
/*
获取一组坐标中,y坐标最小值
*/
public 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 与特定区域的所有边的交点,并去除和顶点重复的,再将交点分为左和右两部分
*/
public 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;
}
}
//实例化对象
$area = new Area($area);
//判断坐标点是否在区域中 如果坐标点在某一区域返回区域编号 否则返回false;
var_dump($area->checkPoint(116.531748,39.944229))