根据GEOHASH,查找附近的人,判断距离远

思路:提取用户经纬度,生成GEOHASH,根据geohash的精度判断距离远近,结合 http://bbs.appcan.cn/forum.php?m ... 7391&extra=page%3D1 这个帖子,可以完全计算出附近的人以及精确的距离(不过微信用的是大致距离,计算精确距离太耗费资源了。)


先普及一下GEOHASH的知识:
http://blog.jobbole.com/80633/  GEO核心原理,懒得复制了,大家移步看一下就好了。

反正就是可以把精度和维度,这两段数字,处理成一段若干长度的字符,这一串字符越长,距离信息越精确,同时,两个人的距离越近,GEOHASH的字符越接近。
比如我在B点,我的经纬度转换成GEOHASH是:45sadfdagfgfa43
你在A店,你的经纬度转换成GEOHASH是:       45sadfdagfgfg34
我可以根据geohash的字符位数情况,判断两点的大致距离。
判断依据:



从上图可以看出,当GEOHASH长度为8的时候,转换成经纬度的长度是20位,那么距离误差是正负0.019千米。
长度为7,那么距离误差是正负0.076千米。
好了。有了这个思路,就OK了。
我们把每个人的经纬度都转换成geohash,根据geohash选择周围的事物距离,比如截取geohash前5位,进行对比,前五位全部相同,说明这些geohash距离是2.4公里以内的(微信的附近人最远就是2.5公里,估计也是这个东西搞出来的)

那么,怎么取得GEOHASH呢?
别的不会,对PHP稍有了解。网上翻了翻,就用PHP的GEOHASH扩展来搞。
首先下载gethub,把他**到自己的服务器里,
https://github.com/shenzhe/geohash
从新编译PHP:
然后到geohash目录。
执行:phpize  (最好带上PHP安装目录的绝对路径)
./configure
make
make install
然后把 geohash.so 加入到php.ini中

装好之后看一下PHPINFO是否成功启用了。

然后就可以直接在PHP页面里转换了:

/** *  $latitude    //纬度
*  $longitude   //经度
*  $precision   //精密度, 默认是12
*  返回 $precision 长度的 string 
*/
geohash_encode($latitude, $longitude, $precision=12);  



/**
*  $hash    //geohash_encode后的值
*  返回 array // Array
*                  (
*                      [latitude] => 39.416916975752
*                      [longitude] => 100.92223992571
*                      [north] => 39.416917059571
*                      [east] => 100.92224009335
*                      [south] => 100.92223992571
*                      [west] => 100.92223975807
*                  )
*/
geohash_decode($hash);

/**
*  $hash    //geohash_encode后的值
*  返回 在$hash 8个 (东南西北各二个)附近的hash值
*/
geohash_neighbors($hash);

/**
*  $precision    //精密度
*  返回 数组,array("width"=>12.0, "height"=>12.0) 
*  表示矩形的宽和高
*/
geohash_dimension($hash);


上面教程是gethub里复制的,有问题可以私信我。
通过geohash_encode取得hash之后,我们可以通过各种方式,将他截取下来,
然后作为KEY,写入redis或者mysql。(强烈建议用redis,用起来太爽了)
我直接写了一个方法,根据各种距离,返回不同的字符长度
        function getgeohash($lat,$log){
                $geohash=geohash_encode($lat,$log,12); //取得原始GEOHASH
                $arr['0']=$geohash;//原始HASH
                $arr['9']=substr($geohash,0,9); //9位hash 距离最精确,
                $arr['8']=substr($geohash,0,8);//8位,距离相对精确,具体精度看上面的表。
                $arr['7']=substr($geohash,0,7);//7位,距离也挺精确
                $arr['6']=substr($geohash,0,6);
                $arr['5']=substr($geohash,0,5);
                $arr['4']=substr($geohash,0,4);//4位hash,只要前4位相同,可以找出附近20KM的人事物。
                return $arr;
        }









redis的PHP扩展不解释了,有疑问的可以私信我。
$redis->sAdd('near:near:'.$arr[4],value);//20KM内的事物集合。

$redis->sAdd('near:near:'.$arr[5],value);//2.4KM内的事物集合。
$redis->sAdd('near:near:'.$arr[6],value);//0.61KM内的事物集合。
$redis->sAdd('near:near:'.$arr[7],value);//0.076KM内的事物集合。
$redis->sAdd('near:near:'.$arr[8],value);//0.019KM内的事物集合。


这里的$value,各位随意存数据,可以是用户信息的json,或者只放一个用户的uid随意啦。
'near:near arr[4-8] 这主要作寻址用,各位可以自己配置自己的路径,是各个数据集的key,根据情况酌情取就行了。


使用:
用户A,登陆后,先写入redis:
$redis->sAdd('near:near:'.$arr[4],value);//20KM内的事物集合。
当用户选择查询周围20KM的事物,
直接取得用户当前的geohash,然后截取geohash前4位。如:$geoarr[4];
然后读取redis
$redis->sort('near:near:'.$geoarr[4]);
这里直接返回了所有20KM内用户的集合。


不明白的私信吧,如果redis搞不定,可以用mysql存。


用redis有一个好处是可以结合sting或者hash等设置过期时间,比如30分钟后自动生效,这个各位自己摸索吧。
我这边的产品未成形,没有完整代码共享。看 杨焕昭 朋友发的:计算两个经纬度之间的直线距离 http://bbs.appcan.cn/forum.php?m ... 1&fromuid=55258


一时脑热,我也发一个,,,很轻松就实现了附近人,或者摇一摇搜索附近人,附近商家,等等好玩的东西。

##############################################################

下面评论的哥们给了一个geohash的github地址

```

    - https://github.com/CloudSide/geohash

```

你可能感兴趣的:(redis)