这篇文章主要为大家详细介绍了PHP中经纬度坐标相关计算方法的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
目录
1. 前言2. 计算经纬度坐标间的距离3. 根据经纬度坐标距离排序4. 经纬度范围查询1. 前言
想要测试本文提供的几个功能函数,可以使用下面这个数据表结构及其数据
CREATE TABLE `user` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',`name` varchar(60) DEFAULT NULL COMMENT '昵称',`longitude` varchar(64) DEFAULT NULL COMMENT '经度',`latitude` varchar(64) DEFAULT NULL COMMENT '纬度',`remark` varchar(50) DEFAULT NULL COMMENT '备注',`distance` varchar(20) DEFAULT NULL COMMENT '距离',PRIMARY KEY (`id`)) ENGINE=InnoDB COMMENT='用户表';INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('中海九号公馆', '113.899529', '22.60063', '深圳市宝安区中海九号公馆', '3.66km');INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('平峦山公园', '113.876462', '22.608322', '深圳市宝安区平峦山公园', '2.88km');INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('铁仔山公园', '113.86359', '22.592355', '深圳市宝安区铁仔山公园', '1.16km');INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('宝安公园', '113.902671', '22.58621', '深圳市宝安区宝安公园', '3.45km');
本文内容测试各个功能函数时,使用的当前位置坐标均为:
// 深圳市宝安区西乡街道九方广场$longitude = '113.869205';//经度$latitude = '22.583286';//纬度
2. 计算经纬度坐标间的距离
计算经纬度坐标间的距离功能函数 (前四个参数为两组经纬度坐标)
/*** 计算经纬度坐标间的距离* @param $lng1 经度* @param $lat1 纬度* @param $lng2 经度* @param $lat2 纬度* @param $lang 语言*/function get_distance($lng1, $lat1, $lng2, $lat2, $lang = 'en'){// 地球的近似半径(单位:米)$earthRadius = 6367000;// 将这些度数转换为弧度以使用公式$lat1 = ($lat1 * pi()) / 180;$lng1 = ($lng1 * pi()) / 180;$lat2 = ($lat2 * pi()) / 180;$lng2 = ($lng2 * pi()) / 180;// 使用 Haversine 公示计算距离// http://en.wikipedia.org/wiki/Haversine_formula$calcLongitude = $lng2 - $lng1;$calcLatitude = $lat2 - $lat1;$stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2);$stepTwo = 2 * asin(min(1, sqrt($stepOne)));// 两个经纬度坐标的距离(单位: 米)$calculatedDistance = round($earthRadius * $stepTwo);// 距离单位$language = ['en' => ['m' => 'm', 'km' => 'km'],'cn' => ['m' => '米', 'km' => '公里'],];if (!isset($language[$lang])) throw new \Exception('不支持的语言:' . $lang);foreach ($language[$lang] as $key => $value) $$key = $value;// 两个坐标间的距离,单位:米$distance = round($calculatedDistance);// 距离单位转换:超出 1000m 时单位转为kmif ($distance < 1000) {$distance .= $m;} else {$distance = floatval(number_format($distance / 1000, 2)) . $km;}return $distance; // 返回单位转换后的距离}
使用示例:
我在 九方广场,手机上的高德地图导航至 中海九号公馆 显示的距离为 3.6公里,计算结果还是很准确的
// 深圳市宝安区西乡街道九方广场: 113.869205, 22.583286// 深圳市宝安区西乡街道中海九号公馆: 113.899529, 22.60063$distance = get_distance(113.869205, 22.583286, 113.899529, 22.60063);echo $distance; //3.66km
3. 根据经纬度坐标距离排序
项目中经常有距离显示数据的场景,根据距离排序,越近越靠前显示;比如: 店铺地址、房源信息等。代码示例:
// 当前坐标$longitude = '113.869205';$latitude = '22.583286';// 数据库中经纬度字段分别为:longitude、latitude$field = '*,( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * (' . $longitude . ' - longitude) / 360 ), 2 ) + COS(PI() * ' . $latitude . ' / 180) * COS(latitude * PI() / 180) * POW( SIN( PI() * (' . $latitude . ' - latitude) / 360 ), 2 ) ) ) ) AS juli';// 根据距离升序查询(越近越靠前)$order = 'juli asc,id desc';// 查询数据Db::name('user')->field($field)->order($order)->select();
4. 经纬度范围查询
经纬度范围计算 功能函数
/*** 经纬度范围计算* @param $longitude 经度* @param $latitude 纬度* @param $radius 半径(米)* @return array*/function get_around($longitude, $latitude, $radius){$PI = 3.14159265;$degree = (24901 * 1609) / 360.0;$dpmLat = 1 / $degree;$radiusLat = $dpmLat * $radius;$minLat = $latitude - $radiusLat;$maxLat = $latitude + $radiusLat;$mpdLng = $degree * cos($latitude * ($PI / 180));$dpmLng = 1 / $mpdLng;$radiusLng = $dpmLng * $radius;$minLng = $longitude - $radiusLng;$maxLng = $longitude + $radiusLng;return compact('minLat', 'maxLat', 'minLng', 'maxLng');}
使用示例
查询 3 公里内的数据。首先,根据当前位置获取 3 公里内的经纬度范围,然后带上查询条件查询数据库即可
$longitude = 113.869205; //经度$latitude = 22.583286; //纬度$radius = 3000; //单位:米// 经纬度范围$around = get_around($longitude, $latitude, $radius);// 构造查询条件// 数据库经纬度字段分别为:longitude,latitude$where = [['longitude', '>=', $around['minLng']],['longitude', '<=', $around['maxLng']],['latitude', '>=', $around['minLat']],['latitude', '<=', $around['maxLat']],];// 按照经纬度范围查询数据// 建议使用 where 的闭包查询(TP6.0)// 因为闭包可以生成以下SQL,标明这几个查询条件是一个整体,便于后期维护// SQL语句示例: SELECT * FROM `user` WHERE ( 经纬度查询条件 ) and 其他条件$data = Db::name('user')->where(function ($query) use ($where) {$query->where($where);})->select();