资讯专栏INFORMATION COLUMN

场景解决方案:附近的人(GeoHash的应用)

yeyan1996 / 3286人阅读

摘要:前言附近的人,这四个字的需求就大有文章可做了。很二逼的做法是,存每个人的经度纬度,然后遍历数据库所有数据循环,两点距离坐标公式。减少扫描行数来实现减轻数据库的压力。两个点的距离越近,他们的编码前缀部分就相同,前缀部分相同越多,代表距离越近。

前言

  附近的人,这四个字的需求就大有文章可做了。很二逼的做法是,存每个人的经度纬度,然后遍历数据库所有数据,foreach循环,两点距离坐标公式。量少的时候,这个没啥问题。量大了,扫描全表 + 经纬度距离运算分分钟拖垮数据库。那么是否有方案可以解决这个痛点呢,今年就来说下Geohash

实现思路

  想要不拖垮数据,要做到能走索引。就是跟你无关的点,不要扫描。减少扫描行数来实现减轻数据库的压力。那么减少扫描行数肯定要想到索引。可是经纬度有两个字段,且查询条件无论怎么写都没办法走索引。那么唯一能想到的就是二维变一维。 geohash基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码,这种方式简单粗暴,可以满足对小规模的数据进行经纬度的检索。两个点的距离越近,他们的编码前缀部分就相同,前缀部分相同越多,代表距离越近。然后我们做数据库扫描的时候 可以 WHERE geohash Like "code%"这样就起到了走索引从而优化了执行效率。

代码思路(PHP)
 -90, "max" => 90))
    {
        $count   = ($data["max"] - $data["min"]) / 2;
        $limit_0 = array(
            "min" => $data["min"],
            "max" => $data["min"] + $count
        );
        $limit_1 = array(
            "min" => $data["min"] + $count,
            "max" => $data["max"]
        );
        $str     .= $num > $limit_1["min"] ? 1 : 0;
        if ($i >= $max_separate_num) {
            return $str;
        } else {
            return self::separate($num, $str, $i + 1, $max_separate_num, $num > $limit_1["min"] ? $limit_1 : $limit_0);
        }
    }

    /**
     * @param $latitude_str 纬度
     * @param $longitude_str 经度
     */
    public static function combination($latitude_str, $longitude_str)
    {
        $str = "";
        for ($i = 0; $i < strlen($longitude_str); $i++) {//根据精度表,可发现维度>=精度
            $str .= $longitude_str{$i};
            if(isset($latitude_str{$i})){
                $str .=  $latitude_str{$i};
            }
        }
        return $str;
    }

    public static function geohash_encode($str)
    {
        $str_arr    = str_split($str, 5);//按5位分割字符串
        $encode_str = "";
        foreach ($str_arr as $va) {
            $decimal    = bindec($va);
            $encode_str .= self::BASE32[$decimal];
        }
        return $encode_str;
    }
    /**
     * 编码
     */

    /**
     * 解码
     */
    public static  function geohash_decode($str)
    {
        //根据一位字符串进行切割
        $str_arr    = str_split($str, 1);
        $decode_str = "";
        $base32     = array_flip(self::BASE32);
        foreach ($str_arr as $va) {
            $decode_str .= str_pad(decbin($base32[$va]),5,"0",STR_PAD_LEFT);
        }
        return (string)$decode_str;

    }

    /**
     * 解码二进制组合
     * @param $str
     * @return array
     */
    public static function de_combination($str)
    {
        $latitude_str  = "";
        $longitude_str = "";
        //根据两位字符串切割
        $str_arr = str_split($str, 2);
        foreach ($str_arr as $va) {
            $longitude_str .= $va[0];
            if(isset($va[1])){//根据精度表,可发现维度>=精度
                $latitude_str  .= $va[1];
            }
        }
        return array(
            self::LATITUDE=>$latitude_str,
            self::LONGITUDE=>$longitude_str,
        );
    }

    /**
     * 解码二分区间
     * @param $str
     * @param string $i//执行次数
     * @param array $data、、区间
     */
    public static function de_separate($str,$i=1,$data = array("min" => -90, "max" => 90)){
        $count   = ($data["max"] - $data["min"]) / 2;
        $limit_0 = array(
            "min" => $data["min"],
            "max" => $data["min"] + $count
        );
        $limit_1 = array(
            "min" => $data["min"] + $count,
            "max" => $data["max"]
        );
        if($str[$i-1]==0){
            $data = $limit_0;
        }else{
            $data = $limit_1;
        }

        if ($i >= strlen($str)) {
            return $data;
        } else {
            return self::de_separate($str, $i + 1, $data);
        }
    }

    /**
     * 解码
     */

    /**
     * 根据精度获取二分次数
     * @param $level
     * @param $type
     */
    public static function get_precision_level_num($level, $type = self::LATITUDE)
    {
        $precision = array(
            1  => array(
                self::LATITUDE  => 2,
                self::LONGITUDE => 3,
            ),
            2  => array(
                self::LATITUDE  => 5,
                self::LONGITUDE => 5,
            ),
            3  => array(
                self::LATITUDE  => 7,
                self::LONGITUDE => 8,
            ),
            4  => array(
                self::LATITUDE  => 10,
                self::LONGITUDE => 10,
            ),
            5  => array(
                self::LATITUDE  => 12,
                self::LONGITUDE => 13,
            ),
            6  => array(
                self::LATITUDE  => 15,
                self::LONGITUDE => 15,
            ),
            7  => array(
                self::LATITUDE  => 17,
                self::LONGITUDE => 18,
            ),
            8  => array(
                self::LATITUDE  => 20,
                self::LONGITUDE => 20,
            ),
            9  => array(
                self::LATITUDE  => 22,
                self::LONGITUDE => 23,
            ),
            10 => array(
                self::LATITUDE  => 25,
                self::LONGITUDE => 25,
            ),
            11 => array(
                self::LATITUDE  => 27,
                self::LONGITUDE => 28,
            ),
            12 => array(
                self::LATITUDE  => 30,
                self::LONGITUDE => 30,
            ),
        );
        return $precision[$level][$type];

    }

    /**
     * 获取区间
     * @param $type
     * @return mixed
     */
    public static function get_interval_value($type = self::LATITUDE)
    {
        $interval = array(
            self::LATITUDE  => array(
                "min" => -90,
                "max" => 90
            ),
            self::LONGITUDE => array(
                "min" => -180,
                "max" => 180
            ),
        );
        return $interval[$type];
    }
}
精度值

如图,当前缀码相同为7相差76米左右,为8相差19米,为9的话可以近似理解为那个人就在你身边了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30978.html

相关文章

  • 【戴嘉乐】基于IPFS和GeoHash构建具有地理位置价值服务DDApp(理论篇)

    摘要:数据将具有如下个特点将二维的经纬度转换成字符串,比如下图展示了北京个区域的字符串,分别是,等等,每一个字符串代表了某一矩形区域。例如,坐标对,位于北京安定门附近,后形成的值为。 作者简介:戴嘉乐( Mr.Maple ) | 前百度高级研发工程师 | IPFS应用实践者&布道师|个人网站:https://www.daijiale.cn联系方式:微信号:daijiale6239。 show...

    lmxdawn 评论0 收藏0
  • 从拿到班车手册.xls到搜索附近班车地点

    摘要:辗转流传出班车手册后发现搜索实在是太不方便了,于是有了一个主义,想做一个可以搜索房子地址,找出附近班车点类似大众点评的定位搜索附近餐馆的功能。 起因 七月份要去某厂报道了,异地租房的时候发现想租一个有公司班车的地方,却不知道哪里有班车。辗转流传出班车手册后发现搜索实在是太不方便了,于是有了一个主义,想做一个可以搜索房子地址,找出附近班车点(类似大众点评的定位搜索附近餐馆的功能)。现在做...

    jhhfft 评论0 收藏0
  • 浅谈附近地点搜索

    摘要:编码的前缀可以表示更大的区域,查找附近的,非常方便。百度拾取坐标地址编码精度为位时,大概为附近千米。在经度相等的情况下纬度每隔度,距离相差约米每隔度,距离相差约米每隔度,距离相差约米每隔度,距离相差约米每隔度,距离相差约米。 随着移动端的普及,很多 App应用 都有 LBS 功能。 附近的银行、 附近的饭店、 附近的超市、 附近的厕所、 ... 以上的需求很类似,实现的原理也大致相同。...

    褰辩话 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<