PHP/JAVA判断点是否在围栏内,百度、高德、腾讯地图
坐标点,多边形区域,判断点是否在区域内
。具体的应用场景如:外卖派送,用户提供的
坐标是否是在外卖的派送范围之内
。用户的坐标可以通过手机设备获取到,派送范围就是通过在地图上,进行多边形的绘制,获取多个坐标点连接起来的配送范围。下面来看看代码上是如何简单判断的。
引射线法:从目标点出发引一条射线,看这条射线和多边形所有边的交点数射线法
适用范围:任意多边形,不需考虑精度误差和多边形点给出的顺序
以被测点Q为端点,向任意方向作射线(一般水平向右作射线),统计该射线与多边形的交点数。如果为奇数,Q在多边形内;如果为偶数,Q在多边形外。
PHP单区域判断点是否在多边形区域围栏内
函数判断多边形区域围栏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?php //php判断点是否在围栏内 function inArea($x, $y, $arr) { //点的数量 $count = count($arr); $n = 0; //点与线相交的个数 $bool = false; //外 for ($i = 0, $j = $count - 1; $i < $count; $j = $i, $i++) { //两个点一条线 取出两个连接点的定点 $px1 = $arr[$i]['jd']; $py1 = $arr[$i]['wd']; $px2 = $arr[$j]['jd']; $py2 = $arr[$j]['wd']; //$x的水平位置画射线 if ($x >= $px1 || $x >= $px2) { //判断$y 是否在线的区域 if (($y >= $py1 && $y <= $py2) || ($y >= $py2 && $y <= $py1)) { if (($y == $py1 && $x == $px1) || ($y == $py2 && $x == $px2)) { #如果$x的值和点的坐标相同 $bool = 2; //在点上 return $bool; } else { $px = $px1 + ($y - $py1) / ($py2 - $py1) * ($px2 - $px1); if ($px == $x) { $bool = 3; //在线上 } elseif ($px < $x) { $n++; } } } } } if ($n % 2 != 0) { $bool = true; } return $bool; } $area_arr = [ [ 'jd' => '113.923664', 'wd' => '30.802822' ], [ 'jd' => '113.959369', 'wd' => '30.393828' ], [ 'jd' => '114.879474', 'wd' => '30.789846' ], [ 'jd' => '114.873981', 'wd' => '30.404488' ], ]; var_dump(inArea('114.286212', '30.550064', $area_arr)); |
phpgeo库判断多边形区域围栏
mjaschen/phpgeo
是一个php的geo的库,提供了一些关于地理经纬度
相关的功能,例如地理围栏、距离计算等。首先composer
安装此包: PHP要求至少大于7
1 |
composer require mjaschen/phpgeo |
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php require './vendor/autoload.php'; //引入两个类 use Location\Coordinate; use Location\Polygon; //绘制一个多边形 $geo = new Polygon(); $geo->addPoint(new Coordinate(39.930131, 116.417301)); $geo->addPoint(new Coordinate(39.930131, 116.377476)); $geo->addPoint(new Coordinate(39.911305, 116.377476)); $geo->addPoint(new Coordinate(39.911305, 116.417301)); //两个坐标做测试 $a = new Coordinate(39.916527, 116.397128); $b = new Coordinate(39.901305, 116.397128); //判断是否在执行的多边形中 if ($geo->contains($a)) { echo "a点在多边形的范围内"; } else { echo "a点不在多边形的范围内"; } echo "<br/>"; if ($geo->contains($b)) { echo "b点在多边形的范围内"; } else { echo "b点不在多边形的范围内"; } |
PHP多区域判断点是否在多边形区域围栏内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
<?php // *** 配置文件(表示区域的三维数组)其内的点,必须按顺时针方向依次给出! $area = array( // 天通苑店 0 => array( array('x' => 116.448275, 'y' => 40.083313), array('x' => 116.441448, 'y' => 40.038418), 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.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) ) ); /* *** 配置文件(表示区域的三维数组)其内的点,必须按顺时针方向依次给出! *** 确定一点是否在一区域(多边形)内: 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)); |
JAVA判断一个点是否在多边形内,射线法,电子围栏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/** * 根据某点坐标判断该坐标是否在某区域坐标范围内 * @param px 目标点x坐标 * @param py 目标点y坐标 * @param polygonXA 目标范围xy坐标集合 * @return */ public boolean rayCasting(double px, double py, String polygon) { boolean flag = false; String[] points = polygon.split(","); for (int i = 0, j = points.length - 2; i < points.length - 1; j = i, i = i + 2) { double sx = Double.parseDouble(points[j]);// 从倒数第二依次读取; double sy = Double.parseDouble(points[j + 1]);// 从倒第一依次读取; double tx = Double.parseDouble(points[i]);// 从第一个依次读取; double ty = Double.parseDouble(points[i + 1]);// 从第二个依次读取; // 点与多边形顶点重合 if ((sx == px && sy == py) || (tx == px && ty == py)) { return true; } // 判断线段两端点是否在射线两侧,射线为y轴; if ((sy < py && ty >= py) || (sy >= py && ty < py)) { // 线段上与射线 Y 坐标相同的点的 X 坐标 double x = sx + (py - sy) * ((tx - sx) / (ty - sy)); // 点在多边形的边上 if (x == px) { return true; } // 射线穿过多边形的边界 if (x > px) { flag = !flag; } } } // 射线穿过多边形边界的次数为奇数时点在多边形内 return flag; } |
原文连接:PHP/JAVA判断点是否在多边形区域围栏内
所有媒体,可在保留署名、
原文连接
的情况下转载,若非则不得使用我方内容。