• 【LBS】基于地理位置的搜索之微信 附近的人 简单实现


    缘由

    本周技术群有一个同学说我们该怎么实现 由近到远的基于地理位置的搜索,我创业做电商的系统做过类似这样的服务,我把我们以前的操作给大家分享下


    什么是LBS

    LBS 全称是 Location  Based Service ,基于位置的服务。我们可以使用到这种服务,真是由于我国移动设备的大量增加,让我们加速进入了 移动互联网的时代。


    由近到远的基于地理位置的搜索  其实就是 通过当前使用用户的经纬度,然后从我们自己的数据库中查出指定范围内(例如5km)的数据,按照由近到远的顺序 进行展示。

    这句话中有四个条件

    • 用户的经纬度,我们定义 用户维度为:$lat 、经度:$lng

    • 指定范围,定义范围:distince

    • 数据库中商家的经纬度字段,定义 维度为:lat,经度:lng

    • 计算 用户经纬度 与 数据库中商家的经纬度 距离,由远及近进行排序


    接下来,我们一起来用两种方案实现


    实现方案一:

    这种方案会比较挫,


    理想模型计算阀值点

    计算某个经纬度的周围某段距离的正方形的四个点


    600


    为什么这么计算,我给大家举个例子,如果我们把我们的地球仪中国区域直接摁平,是不是地球仪就灭有弧度了 ,我们理想方式就是在一定距离上这么计算的。计算代码如下


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


    取出用户指定距离的数据

    根据上面的方法,我们计算出来了4个点,接下来我们直接从数据库取出符合条件的数据


    $geo_data = squarePoint($lat,$lng,$distance);
    $left_bottom =  $geo_data['left-bottom'];
    $right_top = $geo_data['right-top'];
    $lat_min = $left_bottom['lat'];
    $lat_max = $right_top['lat'];
    $lng_min = $left_bottom['lng'];
    $lng_max = $right_top['lng'];
    
    $sql = "SELECT * FROM table_name 
    WHERE lat > {$lat_min} lat < {$lat_max} and lng > {$lng_min} and lng < {$lng_max} ";

    按照距离远近排序

    这个将 上面的符合条件的结果集取出来,在代码中排序,计算 两个经纬度之间距离的方法如下


    /**
     *
     * 根据经纬度计算距离 单位(公里)
     * @param  $lng1 float 经度1
     * @param  $lat1 float 纬度2
     * @param  $lng2 float 经度1
     * @param  $lat2 float 纬度2
     * @return float
     */
    function getdistance($lng1,$lat1,$lng2,$lat2)
    {
       $dx = $lng1 - $lng2; // 经度差值
       $dy = $lat1 - $lat2; // 纬度差值
       $b = ($lat1 + $lat2) / 2.0; // 平均纬度
       $Lx = deg2rad($dx) * cos(deg2rad($b)); // 东西距离
       $Ly = deg2rad($dy); // 南北距离
       return round(6371*sqrt($Lx * $Lx + $Ly * $Ly),4);  // 用平面的矩形对角距离公式计算总距离
    }



    按照方法一计算出来,基本问题不大,但是在数据量大(第二步结果集)到一定程度了是有很严重的效率问题的。这里给出一个衍生版本,直接计算距离


    SELECT *,SQRT( POWER( $lat - lat, 2) + POWER($lng  - lng, 2) ) AS d 
    FROM table_name 
    WHERE (lat BETWEEN $lat_min AND $lat_max ) AND (lng BETWEEN $lng_min AND $lng_max ) AND d < $distance 
    ORDER BY d ASC LIMIT 10;


    实现方案二

    这个方法很快,直接可以SQL实现,由于数据库本身也支持很多函数的,我们直接在数据库本身计算就可以了。具体计算代码如下:


    /**
     * $lat:用户维度
     * $lng:用户精度
     * $as_name:查询出来的SQL字段名称
     */
    get_distance_sql($lat,$lng,$as_name='distance')
    {
       return sprintf('round(6371*sqrt( pow((PI()*(abs(`lat`-%f))/180) * cos(PI()*(`lat`+%f)/360),2) 
       + pow((PI()*abs(`lng`-%f)/180),2)),4) as %s',$lat,$lat,$lng,$as_name);
    }


    具体SQL如下:

    /**
    * shop_id 商家id
    */
    $sql = "SELECT shop_id,lat,lng,".get_distance_sql( $lat,$lng)." FROM table_name 
    WHERE distance< = ".$distance." ORDER BY distance ASC" ;


    这样就可以直接查出结果并排序了

    结论

    以上两种方案中,第二个是我我们当时使用的,我们当时数据库几十万,效率还可以。上百万乃至更高的需要大家去实现了




    原文地址:【LBS】基于地理位置的搜索之微信 附近的人 简单实现
    标签:lbs   附近的人   由远及近   大众点评   

    智能推荐

  • 相关阅读:
    python2 类型转换和数值操作
    python2 实现的LED大数字效果
    Python2 基础语法(三)
    delphi操作ini文件
    [SQL]数据库还原 42000错误
    我的宝宝来了
    [DELPHI] D2009控件的安装
    DELPHI学习过程和函数
    [SQL][转载]SQL优化索引
    [SQL] SQL语句,存储过程执行时间
  • 原文地址:https://www.cnblogs.com/apanly/p/7258048.html
Copyright © 2020-2023  润新知