PHP 经纬度坐标相关计算方法

本文介绍了如何在PHP中实现计算经纬度坐标间距离、根据距离排序、以及进行经纬度范围查询的功能,包括使用Haversine公式和针对MySQL数据库的操作,以满足项目中展示地理位置信息的需求。
摘要由CSDN通过智能技术生成

​​​​

1. 前言

想要测试本文提供的几个功能函数,可以使用下面这个数据表结构及其数据

 
  1. CREATE TABLE `user` (
  2. `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',
  3. `name` varchar(60) DEFAULT NULL COMMENT '昵称',
  4. `longitude` varchar(64) DEFAULT NULL COMMENT '经度',
  5. `latitude` varchar(64) DEFAULT NULL COMMENT '纬度',
  6. `remark` varchar(50) DEFAULT NULL COMMENT '备注',
  7. `distance` varchar(20) DEFAULT NULL COMMENT '距离',
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB COMMENT='用户表';
  10. INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('中海九号公馆', '113.899529', '22.60063', '深圳市宝安区中海九号公馆', '3.66km');
  11. INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('平峦山公园', '113.876462', '22.608322', '深圳市宝安区平峦山公园', '2.88km');
  12. INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('铁仔山公园', '113.86359', '22.592355', '深圳市宝安区铁仔山公园', '1.16km');
  13. INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('宝安公园', '113.902671', '22.58621', '深圳市宝安区宝安公园', '3.45km');

本文内容测试各个功能函数时,使用的当前位置坐标均为:

 
  1. // 深圳市宝安区西乡街道九方广场
  2. $longitude = '113.869205';//经度
  3. $latitude = '22.583286';//纬度
2. 计算经纬度坐标间的距离

计算经纬度坐标间的距离 功能函数 (前四个参数为两组经纬度坐标)

 
  1. /**
  2. * 计算经纬度坐标间的距离
  3. * @param $lng1 经度
  4. * @param $lat1 纬度
  5. * @param $lng2 经度
  6. * @param $lat2 纬度
  7. * @param $lang 语言
  8. */
  9. function get_distance($lng1, $lat1, $lng2, $lat2, $lang = 'en')
  10. {
  11. // 地球的近似半径(单位:米)
  12. $earthRadius = 6367000;
  13. // 将这些度数转换为弧度以使用公式
  14. $lat1 = ($lat1 * pi()) / 180;
  15. $lng1 = ($lng1 * pi()) / 180;
  16. $lat2 = ($lat2 * pi()) / 180;
  17. $lng2 = ($lng2 * pi()) / 180;
  18. // 使用 Haversine 公示计算距离
  19. // http://en.wikipedia.org/wiki/Haversine_formula
  20. $calcLongitude = $lng2 - $lng1;
  21. $calcLatitude = $lat2 - $lat1;
  22. $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2);
  23. $stepTwo = 2 * asin(min(1, sqrt($stepOne)));
  24. // 两个经纬度坐标的距离(单位: 米)
  25. $calculatedDistance = round($earthRadius * $stepTwo);
  26. // 距离单位
  27. $language = [
  28. 'en' => ['m' => 'm', 'km' => 'km'],
  29. 'cn' => ['m' => '米', 'km' => '公里'],
  30. ];
  31. if (!isset($language[$lang])) throw new \Exception('不支持的语言:' . $lang);
  32. foreach ($language[$lang] as $key => $value) $$key = $value;
  33. // 两个坐标间的距离,单位:米
  34. $distance = round($calculatedDistance);
  35. // 距离单位转换:超出 1000m 时单位转为km
  36. if ($distance < 1000) {
  37. $distance .= $m;
  38. } else {
  39. $distance = floatval(number_format($distance / 1000, 2)) . $km;
  40. }
  41. return $distance; // 返回单位转换后的距离
  42. }

使用示例:

我在 九方广场,手机上的高德地图导航至 中海九号公馆 显示的距离为 3.6公里,计算结果还是很准确的

 
  1. // 深圳市宝安区西乡街道九方广场: 113.869205, 22.583286
  2. // 深圳市宝安区西乡街道中海九号公馆: 113.899529, 22.60063
  3. $distance = get_distance(113.869205, 22.583286, 113.899529, 22.60063);
  4. echo $distance; //3.66km
3. 根据经纬度坐标距离排序

项目中经常有距离显示数据的场景,根据距离排序,越近越靠前显示;比如: 店铺地址、房源信息等。代码示例:

 
  1. // 当前坐标
  2. $longitude = '113.869205';
  3. $latitude = '22.583286';
  4. // 数据库中经纬度字段分别为:longitude、latitude
  5. $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';
  6. // 根据距离升序查询(越近越靠前)
  7. $order = 'juli asc,id desc';
  8. // 查询数据
  9. Db::name('user')->field($field)->order($order)->select();
4. 经纬度范围查询

经纬度范围计算 功能函数

 
  1. /**
  2. * 经纬度范围计算
  3. * @param $longitude 经度
  4. * @param $latitude 纬度
  5. * @param $radius 半径(米)
  6. * @return array
  7. */
  8. function get_around($longitude, $latitude, $radius)
  9. {
  10. $PI = 3.14159265;
  11. $degree = (24901 * 1609) / 360.0;
  12. $dpmLat = 1 / $degree;
  13. $radiusLat = $dpmLat * $radius;
  14. $minLat = $latitude - $radiusLat;
  15. $maxLat = $latitude + $radiusLat;
  16. $mpdLng = $degree * cos($latitude * ($PI / 180));
  17. $dpmLng = 1 / $mpdLng;
  18. $radiusLng = $dpmLng * $radius;
  19. $minLng = $longitude - $radiusLng;
  20. $maxLng = $longitude + $radiusLng;
  21. return compact('minLat', 'maxLat', 'minLng', 'maxLng');
  22. }

使用示例

查询 3 公里内的数据。首先,根据当前位置获取 3 公里内的经纬度范围,然后带上查询条件查询数据库即可

 
  1. $longitude = 113.869205; //经度
  2. $latitude = 22.583286; //纬度
  3. $radius = 3000; //单位:米
  4. // 经纬度范围
  5. $around = get_around($longitude, $latitude, $radius);
  6. // 构造查询条件
  7. // 数据库经纬度字段分别为:longitude,latitude
  8. $where = [
  9. ['longitude', '>=', $around['minLng']],
  10. ['longitude', '<=', $around['maxLng']],
  11. ['latitude', '>=', $around['minLat']],
  12. ['latitude', '<=', $around['maxLat']],
  13. ];
  14. // 按照经纬度范围查询数据
  15. // 建议使用 where 的闭包查询(TP6.0)
  16. // 因为闭包可以生成以下SQL,标明这几个查询条件是一个整体,便于后期维护
  17. // SQL语句示例: SELECT * FROM `user` WHERE ( 经纬度查询条件 ) and 其他条件
  18. $data = Db::name('user')
  19. ->where(function ($query) use ($where) {
  20. $query->where($where);
  21. })
  22. ->select();
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超酷的站长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值