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


    1. 前言

    PHP 全栈技术群

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

    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');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    // 深圳市宝安区西乡街道九方广场
    $longitude = '113.869205';//经度
    $latitude  = '22.583286';//纬度
    
    • 1
    • 2
    • 3
    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 时单位转为km
        if ($distance < 1000) {
            $distance .= $m;
        } else {
            $distance = floatval(number_format($distance / 1000, 2)) . $km;
        }
        return $distance; // 返回单位转换后的距离
    }
    
    • 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

    使用示例:

    我在 九方广场,手机上的高德地图导航至 中海九号公馆 显示的距离为 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
    
    • 1
    • 2
    • 3
    • 4
    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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    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');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    使用示例

    查询 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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    Linux命令 —— df、du
    Practical+Reverse+Engineering第三章List习题
    Netty第三部
    Linux小知识---CMake的使用
    前端bootstrap+fileInput+django后端是用ajax方式上传form表单数据及多个文件保存到数据库
    遥感图像云检测数据集CHLandsat8
    [附源码]计算机毕业设计JAVASSM归途中流浪动物收容与领养管理系统
    OpenCV学习笔记-环境搭建
    ClassUtils.getClassFileName()方法具有什么功能呢?
    机器学习_LGB调参汇总(开箱即食)
  • 原文地址:https://blog.csdn.net/qq_40994734/article/details/126837739