定位地理位置PHP判断经纬度是否在配送范围之内
发布时间:2018-10-26, 09:10:14 分类:PHP | 编辑 off 网址 | 辅助
图集1/1
正文 6302字数 721,718阅读
php 判断经纬度php 判断一个点的经纬度是否在多边形或圆里
微信小程序定位经纬度和百度经纬度
百度地图PC端判断用户是否在配送范围内
百度地图拾取坐标系统
维度,经度
<?php
if(!$_GET['longitude'] || !$_GET['latitude']) exit('-1');
// longitude 经度
// latitude 维度
class Convert{
private $PI = 3.14159265358979324;
private $x_pi = 0;
public function __construct()
{
$this->x_pi = 3.14159265358979324 * 3000.0 / 180.0;
}
/**
* 判断一个坐标是否在圆内
* 思路:判断此点的经纬度到圆心的距离 然后和半径做比较
* 如果此点刚好在圆上 则返回true
* @param $point ['lng'=>'','lat'=>''] array指定点的坐标
* @param $circle array ['center'=>['lng'=>'','lat'=>''],'radius'=>''] 中心点和半径
*/
function is_point_in_circle($point, $circle){
$distance = $this -> distance($point['lat'],$point['lng'],$circle['center']['lat'],$circle['center']['lng']);
if($distance <= $circle['radius']){
return true;
}else{
return false;
}
}
/**
* 计算两个点之间的距离
* @param $latA 第一个点的纬度
* @param $lonA 第一个点的经度
* @param $latB 第二个点的纬度
* @param $lonB 第二个点的经度
* @return float
*/
function distance($latA, $lonA, $latB, $lonB)
{
$earthR = 6371000.;
$x = cos($latA * $this->PI / 180.) * cos($latB * $this->PI / 180.) * cos(($lonA - $lonB) * $this->PI / 180);
$y = sin($latA * $this->PI / 180.) * sin($latB * $this->PI / 180.);
$s = $x + $y;
if ($s > 1) $s = 1;
if ($s < -1) $s = -1;
$alpha = acos($s);
$distance = $alpha * $earthR;
return $distance;
}
/**
* 判断一个坐标是否在一个多边形内(由多个坐标围成的)
* 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
* 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
* @param $point 指定点坐标
* @param $pts 多边形坐标 顺时针方向
*/
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['lng'] == $p1['lng'] && $p['lat'] == $p1['lat']) {
return $boundOrVertex;//p is an vertex
}
$p2 = $pts[$i % $N];//right vertex
if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) {//ray is outside of our interests
$p1 = $p2;
continue;//next ray left point
}
if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) {//ray is crossing over by the algorithm (common part of)
if($p['lng'] <= max($p1['lng'], $p2['lng'])){//x is before of ray
if ($p1['lat'] == $p2['lat'] && $p['lng'] >= min($p1['lng'], $p2['lng'])) {//overlies on a horizontal ray
return $boundOrVertex;
}
if ($p1['lng'] == $p2['lng']) {//ray is vertical
if ($p1['lng'] == $p['lng']) {//overlies on a vertical ray
return $boundOrVertex;
} else {//before ray
++$intersectCount;
}
} else {//cross point on the left side
$xinters = ($p['lat'] - $p1['lat']) * ($p2['lng'] - $p1['lng']) / ($p2['lat'] - $p1['lat']) + $p1['lng'];//cross point of lng
if (abs($p['lng'] - $xinters) < $precision) {//overlies on a ray
return $boundOrVertex;
}
if ($p['lng'] < $xinters) {//before ray
++$intersectCount;
}
}
}
} else {//special case when ray is crossing through the vertex
if ($p['lat'] == $p2['lat'] && $p['lng'] <= $p2['lng']) {//p crossing over p2
$p3 = $pts[($i+1) % $N]; //next vertex
if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) { //p.lat lies between p1.lat & p3.lat
++$intersectCount;
} else {
$intersectCount += 2;
}
}
}
$p1 = $p2;//next ray left point
}
if ($intersectCount % 2 == 0) {//偶数在多边形外
return false;
} else { //奇数在多边形内
return true;
}
}
}
$point = ['lng'=>$_GET['longitude'],'lat'=>$_GET['latitude']];
/*$circle = [
'center'=>['lng'=>116.12637,'lat'=>40.114308],
'radius'=>46807.83038795571
];*/
$convert = new Convert();
//$bool = $convert -> is_point_in_circle($point,$circle);
//var_dump($bool);
$pts = [
['lng'=>108.326212, 'lat'=>22.891039],
['lng'=>108.274757, 'lat'=>22.890107],
['lng'=>108.22359, 'lat'=>22.856143],
['lng'=>108.235304, 'lat'=>22.791654],
['lng'=>108.247449, 'lat'=>22.769528],
['lng'=>108.276697, 'lat'=>22.755665],
['lng'=>108.323769, 'lat'=>22.754065],
['lng'=>108.334189, 'lat'=>22.722067],
['lng'=>108.390531, 'lat'=>22.7332],
['lng'=>108.441986, 'lat'=>22.761664],
['lng'=>108.497609, 'lat'=>22.787922],
['lng'=>108.494375, 'lat'=>22.821637],
['lng'=>108.446441, 'lat'=>22.819638],
['lng'=>108.42711, 'lat'=>22.85128],
['lng'=>108.375295, 'lat'=>22.878187],
['lng'=>108.326217, 'lat'=>22.891035],
['lng'=>108.326203, 'lat'=>22.891039],
['lng'=>108.326203, 'lat'=>22.891039],
['lng'=>108.326203, 'lat'=>22.891039]
];
//$point = ['lng'=>115.989864,'lat'=>39.973272];
$bool = $convert -> is_point_in_polygon($point,$pts);
var_dump($bool);
Run code
Cut to clipboard
(支付宝)给作者钱财以资鼓励 (微信)→
有过 2 条评论 »
<?php // *** 配置文件(表示区域的三维数组)其内的点,必须按顺时针方向依次给出! $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) ), //大悦城店 3 => array( array('x'=>116.496424,'y'=>39.96253), array('x'=>116.479527,'y'=>39.975491), array('x'=>116.492921,'y'=>39.972491), array('x'=>116.508533,'y'=>39.974454), array('x'=>116.535231,'y'=>39.980225), array('x'=>116.553485,'y'=>39.975691), array('x'=>116.564624,'y'=>39.975028), array('x'=>116.571307,'y'=>39.972097), array('x'=>116.571666,'y'=>39.946989), array('x'=>116.547736,'y'=>39.947763), array('x'=>116.549245,'y'=>39.936755), array('x'=>116.564624,'y'=>39.937142), array('x'=>116.569367,'y'=>39.92995), array('x'=>116.570085,'y'=>39.915175), array('x'=>116.496424,'y'=>39.914843) ), //北洼路 4 => array( array('x'=>116.24763, 'y'=>39.978677), array('x'=>116.280975,'y'=>39.976244), array('x'=>116.322872,'y'=>39.991226), array('x'=>116.323231,'y'=>39.986859), array('x'=>116.339975,'y'=>39.986859), array('x'=>116.340263,'y'=>39.982215), array('x'=>116.346443,'y'=>39.98216), array('x'=>116.354492,'y'=>39.963415), array('x'=>116.361822,'y'=>39.963637), array('x'=>116.362397,'y'=>39.957664), array('x'=>116.37792, 'y'=>39.958106), array('x'=>116.37138, 'y'=>39.929728), array('x'=>116.367068,'y'=>39.929341), array('x'=>116.366637,'y'=>39.924361), array('x'=>116.37138, 'y'=>39.924361), array('x'=>116.369655,'y'=>39.913626), array('x'=>116.362325,'y'=>39.912962), array('x'=>116.363188,'y'=>39.903774), array('x'=>116.317194,'y'=>39.902999), array('x'=>116.259415,'y'=>39.902778) ), //安贞店 5 => array( array('x'=>116.372171,'y'=>39.993161), array('x'=>116.443676,'y'=>39.994985), array('x'=>116.448419,'y'=>39.993548), array('x'=>116.43807, 'y'=>39.97735), array('x'=>116.438789,'y'=>39.9656), array('x'=>116.440298,'y'=>39.955839), array('x'=>116.42262, 'y'=>39.955673), array('x'=>116.378531,'y'=>39.954815), array('x'=>116.377704,'y'=>39.963941), array('x'=>116.368003,'y'=>39.96383), array('x'=>116.367679,'y'=>39.973341), array('x'=>116.361247,'y'=>39.973479), array('x'=>116.360529,'y'=>39.987025), array('x'=>116.37235, 'y'=>39.987716) ), //三元桥 6 => array( array('x'=>116.283706,'y'=>40.114137 ), array('x'=>116.354995,'y'=>40.121613 ), array('x'=>116.369081,'y'=>40.114661 ), array('x'=>116.37871, 'y'=>40.114772 ), array('x'=>116.380435,'y'=>40.106826 ), array('x'=>116.385897,'y'=>40.107543 ), array('x'=>116.389346,'y'=>40.07782 ), array('x'=>116.370949,'y'=>40.075998 ), array('x'=>116.37174, 'y'=>40.05739 ), array('x'=>116.325746,'y'=>40.042643 ) ), //团结湖 7 => array( array('x'=>116.43189, 'y'=>39.955341), array('x'=>116.440316,'y'=>39.955396), array('x'=>116.438394,'y'=>39.977225), array('x'=>116.442849,'y'=>39.984116), array('x'=>116.467876,'y'=>39.969194), array('x'=>116.476608,'y'=>39.976797), array('x'=>116.494969,'y'=>39.963637), array('x'=>116.496999,'y'=>39.914179), array('x'=>116.433902,'y'=>39.914511) ), //劲松店 8 => array( array('x'=>116.4053, 'y'=>39.90632 ), array('x'=>116.495418,'y'=>39.911412 ), array('x'=>116.495418,'y'=>39.87709 ), array('x'=>116.491322,'y'=>39.854717 ), array('x'=>116.462432,'y'=>39.851892 ), array('x'=>116.421362,'y'=>39.852141 ), array('x'=>116.420895,'y'=>39.863412 ), array('x'=>116.406809,'y'=>39.863412 ), array('x'=>116.406881,'y'=>39.863357 ) ), //黄村店 9 => array( array('x'=>116.280184,'y'=>39.776406 ), array('x'=>116.314751,'y'=>39.788383 ), array('x'=>116.337586,'y'=>39.805041 ), array('x'=>116.353252,'y'=>39.804487 ), array('x'=>116.356001,'y'=>39.794703 ), array('x'=>116.364912,'y'=>39.799138 ), array('x'=>116.367787,'y'=>39.783836 ), array('x'=>116.378495,'y'=>39.781507 ), array('x'=>116.383094,'y'=>39.766479 ), array('x'=>116.388628,'y'=>39.76426 ), array('x'=>116.387478,'y'=>39.749616 ), array('x'=>116.383238,'y'=>39.748507 ), array('x'=>116.385178,'y'=>39.728255 ), array('x'=>116.352408,'y'=>39.727423 ), array('x'=>116.335592,'y'=>39.705888 ), array('x'=>116.301726,'y'=>39.697727 ) ) ); /* *** 配置文件(表示区域的三维数组)其内的点,必须按顺时针方向依次给出! *** 确定一点是否在一区域(多边形)内: 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; } } $area = new Area($area); var_dump($area->checkPoint(116.531748,39.944229)); ?>