zoukankan      html  css  js  c++  java
  • PHP Mysql 根据一个给定经纬度的点,进行附近地点查询–算法 转载

      几个星期以前的一个项目,需求是根据当前用户上传的经纬度坐标,在数据库几十万万条数据中查询出符合“周围3公里范围内”条件的坐标点。

      Mysql本身是支持空间索引的,但是在5.X版本中取消了Distance()和Related(),无法使用空间的距离函数去直接查询距离在一定范围内的点。所以,我首先想到的是,对每条数据去进行遍历,跟数据库中的每个点进行距离计算,当距离小于3公里时候,认为匹配成功。经测试,这样做确实能得到结果,但是效率极其低下,因为每条数据都得去和数据库中的几十万条数据进行比对,其耗费的时间可想而知。对于这种情况,是用户所无法忍受的。

      后来经过自己的仔细考虑,以及查询各种资料,想到一种利用正方形将方圆3公里这个圆包围起来。利用正方形的四个点,去和用户上传的经纬度进行比较。由此,问题转向了,如何计算正方形四个点经纬度的问题!

      无意中看到一个附近地点搜索初探的帖子,里面使用python实现了计算四个点经纬度的方法。由此,我将其用PHP的方式实现了。

      其实现原理也是很相似的,先计算出当前点周围的正方形的四个点,然后使用经纬度直接去数据库匹配数据。

      假设已知点的经纬度分别为lng,lng,lat
      先实现经度范围的查询,
      在haversin公式中令φ1 = φ2,可得:
          
         用PHP实现的整体方式,就是:

    /**
         * 计算某个经纬度的周围某段距离的正方形的四个点
         *
         * @param
         *            radius 地球半径 平均6371km
         * @param
         *            lng float 经度
         * @param
         *            lat float 纬度
         * @param
         *            distance float 该点所在圆的半径,该圆与此正方形内切,默认值为1千米
         * @return array 正方形的四个点的经纬度坐标
         */
        public function returnSquarePoint($lng, $lat, $distance = 1, $radius = 6371)
        {
            $dlng = 2 * asin(sin($distance / (2 * $radius)) / cos(deg2rad($lat)));
            $dlng = rad2deg($dlng);
    
            $dlat = $distance / $radius;
            $dlat = rad2deg($dlat);
    
            return array(
                'left-top' => array(
                    'lat' => $lat + $dlat,
                    'lng' => $lng - $dlng
                ),
                'right-top' => array(
                    'lat' => $lat + $dlat,
                    'lng' => $lng + $dlng
                ),
                'left-bottom' => array(
                    'lat' => $lat - $dlat,
                    'lng' => $lng - $dlng
                ),
                'right-bottom' => array(
                    'lat' => $lat - $dlat,
                    'lng' => $lng + $dlng
                )
            );
        }

    匹配路线时候就可以采取一下办法(截取当时写的方法,大家理解就好)

    $array[0]就是用户上传的起点终点坐标数组 
     $start = $this->returnSquarePoint($array[0]['start_lng'], $array[0]['start_lat']);
    下面是匹配方法,只是代码截取,请谅解!
      ->andwhere([
                          '>',
                          'start_lat',
                          $start['right-bottom']['lat']
                      ])
                          ->andWhere([
                          '<',
                          'start_lat',
                             
                         $start['left-top']['lat']
                     ])
                         ->andWhere([
                         '>',
                         'start_lng',
                         $start['left-top']['lng']
                     ])
                         ->andWhere([
                         '<',
                         'start_lng',
                         $start['right-bottom']['lng']
                     ]);

    在lat和lng上建立一个联合索引后,使用此项查询,运行效率飞涨。

    总结:这应该也不是效率最好的办法,但是效率比以前确实有明显的提升。大家如果有什么刚好的解决办法,欢迎留言学习。

    原文:https://www.cnblogs.com/John727/p/4514503.html

  • 相关阅读:
    2019.6.20刷题统计
    36 线程 队列 守护线程 互斥锁 死锁 可重入锁 信号量
    35 守护进程 互斥锁 IPC 共享内存 的方式 生产者消费者模型
    34 进程 pid ppid 并发与并行,阻塞与非阻塞 join函数 process对象 孤儿进程与僵尸进程
    33 udp 域名 进程
    32 粘包 文件传输
    31 socket客户端. 服务器 异常 语法
    30 网络编程
    29 元类 异常
    26 封装 反射 常用内置函数
  • 原文地址:https://www.cnblogs.com/gyxdbk/p/14078308.html
Copyright © 2011-2022 走看看