1、背景:領導要求實現 百度或騰訊 地址圍欄 功能;
2、目的:服務端提供一個查詢 地點是否在指定配送區域內的接口
3、使用地圖:騰訊地圖
4、參考連接:
1、https://blog.csdn.net/yilukuangpao/article/details/80995852
2、https://lbs.qq.com/webservice_v1/index.html
5、實現效果:
其中透明藍:免費配送;透明紫:收費配送
6、接口調用代碼: (結果經過嚴格測試,精確!)
<?php
/**
* Time: 9:35
*/
include_once './Convert.php';
$point = ['lng'=>116.394299,'lat'=>40.011674];
$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'=>115.934109, 'lat'=>40.124809],
['lng'=>116.554836, 'lat'=>40.177293],
['lng'=>116.620754, 'lat'=>39.783734],
['lng'=>116.054958, 'lat'=>39.809057]
];
$point = ['lng'=>115.989864,'lat'=>39.973272];
$bool = $convert -> is_point_in_polygon($point,$pts);
var_dump($bool);
7、Convert.php 封裝類代碼:
<?php /** * Created by PhpStorm. * User: 申大俠 * Date: 2018/7/11 * Time: 9:19 */ 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; } } }