lbs(查看附近的人),看看社交软件如何实现查看附近的人
最近在做一款移動端棋牌游戲,為了進一步提高用戶體驗、拉近玩家的距離,我們決定在游戲中加入好友功能,而對于體驗好友功能的玩家來說,要是玩牌的時候可以看看附近都有誰在玩牌,跟他們交流交流玩牌心得什么的無疑是個不錯的想法。而要實現(xiàn)查看附近的人就需要提提LBS(Location Based Service),他的意思就是基于位置的服務(wù),就是通過移動終端獲取到許多用戶或者物體的經(jīng)緯度坐標(biāo),通過這些位置信息所提供的服務(wù)。
好了,扯了這么多,我們來看看如何實現(xiàn)查看附近人的功能的:
首先要具備下面這些環(huán)境:
php+MySQL(MySQL不是必須,本文中用的是redis來存儲用戶的信息)
redis(本文用的是redis,當(dāng)然你也可以用MySQL)
geohash.class.php類(這個類是用來處理經(jīng)緯度坐標(biāo)的一些基本函數(shù),當(dāng)然這些東西完全可以自己去寫,如果時間充裕的話)
好了,等這些環(huán)境都具備了之后,我來講講這個實現(xiàn)過程:
首先介紹下GeoHash思想
第一步. 編碼
這個功能應(yīng)用到了一個很好的算法GeoHash,也許有同學(xué)聽過這個功能,沒錯GeoHash就是通過一個巧妙的算法(不由得驚嘆前輩們真牛!)把經(jīng)緯度轉(zhuǎn)化為字符串,這樣有什么好處呢,顯而易見,將二維的數(shù)據(jù)轉(zhuǎn)化為了一維,這樣一來存儲就方便了,搜索效率也會高很多,那么現(xiàn)在問題來了,GeoHash算法是如何把經(jīng)緯度坐標(biāo)轉(zhuǎn)化為字符串的?
將經(jīng)緯度編碼為字符串的過程可以分為以下3個步驟:
首先就是編碼,對于經(jīng)緯度的編碼通過折半比較法,當(dāng)大于中值時該位編碼為1(小于時編碼為0),下次新的區(qū)間為中值到最大值(或者最小值到中值),這樣一直比較下去,直到到達要求的精度,精度和緯度的方法是一樣的,只不過一個原始區(qū)間是(-90,90),一個是(-180,180),光說不好理解,下面我們看看一個簡單的例子:
對經(jīng)度32.165進行編碼: 對緯度89.156進行編碼:
|
編碼 |
min |
mid |
max |
|
1 |
-90 |
0 |
90 |
|
0 |
0 |
45 |
90 |
|
1 |
0 |
22.5 |
45 |
|
0 |
22.5 |
033.75 |
45 |
|
1 |
22.5 |
28.125 |
33.75 |
|
1 |
28.125 |
30.9375 |
33.75 |
|
0 |
30.9375 |
32.34375 |
33.75 |
|
1 |
30.9375 |
31.640625 |
32.34375 |
|
1 |
31.640625 |
31.9921875 |
32.34375 |
|
0 |
31.9921875 |
32.16796875 |
32.34375 |
|
編碼 |
min |
mid |
max |
|
1 |
-180 |
0 |
180 |
|
0 |
0 |
90 |
180 |
|
1 |
0 |
45 |
90 |
|
1 |
45 |
67.5 |
90 |
|
1 |
67.5 |
78.75 |
90 |
|
1 |
78.75 |
84.375 |
90 |
|
1 |
84.375 |
87.1875 |
90 |
|
1 |
87.1875 |
88.59375 |
90 |
|
0 |
88.59375 |
89.296875 |
90 |
|
1 |
88.59375 |
88.9453125 |
89.296875 |
這樣便可將(89.156,32.165) => (10101 10110,10111 11101)
這個時候就到了第二步驟——組碼了,顧名思義,將第一步產(chǎn)生的編碼組合起來為下一步產(chǎn)生字符串做準(zhǔn)備,組碼的方式是偶數(shù)位放置經(jīng)度,奇數(shù)位放緯度(為什么要這么做呢,我猜可能是谷歌為了大家統(tǒng)一規(guī)范,僅此而已,其實奇偶數(shù)位互換也可以的),對于上面的經(jīng)緯度編碼后再組碼如下:
|
經(jīng)度:10101 11101 |
緯度:10111 11101 |
|
位置編碼:11001 11011 11111 10011 |
圖 3
對于上面的位置編碼,為什么要這么編碼呢,為什么要奇數(shù)位放緯度,偶數(shù)為方經(jīng)度呢,我們看看下面這張圖,用這張圖模擬地圖的經(jīng)緯度,A點(-180,90),B點(180,90),C點(-180,-90),D點(180,-90);
|
A |
B |
||||||||
|
010101 |
010111 |
011101 |
011111 |
110101 |
110111 |
111101 |
111111 |
||
|
010100 |
010110 |
011100 |
011110 |
110100 |
110110 |
111100 |
111110 |
||
|
010001 |
010011 |
011001 |
011011 |
110001 |
110011 |
111001 |
111011 |
||
|
010000 |
010010 |
011000 |
011010 |
110000 |
110010 |
111000 |
111010 |
||
|
000101 |
000111 |
001101 |
001111 |
100101 |
100111 |
101101 |
101111 |
||
|
000100 |
000110 |
001100 |
001110 |
100100 |
100110 |
101100 |
101110 |
||
|
000001 |
000011 |
001001 |
001011 |
100001 |
100011 |
101001 |
101011 |
||
|
000000 |
000010 |
001000 |
001010 |
100000 |
100010 |
101000 |
101010 |
||
|
C |
D |
圖 4
如圖4所示,這樣就可以將地圖(經(jīng)度-180~180,緯度-90~90)分為很多很多多的小塊,每一個小塊都有唯一的二進制編碼,當(dāng)位數(shù)達到一定的長度時就可以表示很小的一塊區(qū)域,這不就可以根據(jù)二進制編碼定位一個唯一的位置了嗎,對于劃分的進一步理解可看下面的圖。
圖 5
如圖5所示,左邊是是對緯度(-180,180)的劃分,可以看出通過劃分可以確定(22.4,45)這一緯度區(qū)間的編碼為1001,當(dāng)然了位數(shù)越多精度越高,同理對經(jīng)度進行劃分,可以確定(-78.75,-67.5)這一經(jīng)度范圍的編碼為0001,可以想象,當(dāng)左右兩張圖合在一起時就可以確定一個唯一的矩形區(qū)域,當(dāng)該區(qū)域足夠小的時候就可一看做一個點。
第二步. 組碼
從圖3可以看出我們對經(jīng)緯度編碼后可得二進制字符串11001 11011 11111 10011
最后使用用0-9、b-z(去掉a, i, l, o)這32個字母進行base32編碼,首先將11001 11011 11111 10011轉(zhuǎn)成十進制,對應(yīng)著25、27、32、19,十進制對應(yīng)的編碼就是tvzm。同理,將編碼轉(zhuǎn)換成經(jīng)緯度的解碼算法與之相反,具體不再贅述。至此,我們的對geohash有了個大致的了解。
圖 6
如何具體的應(yīng)用到程序中
首先思考一下查看附近的人的流程:
用戶點擊查看附近的人按鈕,首先獲取到該用戶的選位置信息(經(jīng)緯度),傳給服務(wù)器。
服務(wù)器收到數(shù)據(jù)之后對該用戶的位置信息進行g(shù)eohash計算,獲得該用戶的位置hash字符串。
對該用戶的位置信息hash串進行緩存(緩存時間長短根據(jù)具體情況而定)。
根據(jù)該hash串選出附近的人。
對hash進行解碼,計算出附近用戶的位置,返回給用戶。
首先看看geohash.class.php這個公共類庫里面的基本方法:
|
[public]Geohash() 初始化hash映射表 |
|
|
Geohash |
[public]encode($lat,$long) 對經(jīng)緯度進行編碼 |
|
[public]decode() 對hash進行解碼 |
圖 7
如圖7所示,顯而易見這個類庫里面有3個函數(shù),第一個用來初始化hash映射表,其實就是把0123456789bcdefghjkmnpqrstuvwxyz字符串中的每個字符和它對應(yīng)的二進制編碼對應(yīng)起來(左邊補零至5位)。encode()是用來生成hash的,decode是用來解碼hash得到hash對應(yīng)的經(jīng)緯度的。
下面我們看個例子,現(xiàn)在假設(shè)有圖8中的幾個用戶查看附近的人:
|
mid |
坐標(biāo) |
|
100 |
(42.61233,-5.61234) |
|
101 |
(-20.25689, 50.56897) |
|
102 |
(10.11233, 57.21234) |
|
103 |
(49.26343, -123.26895) |
|
104 |
(0.00534, -179.56732) |
|
105 |
(-30.55555, 0.28958) |
|
106 |
(5.00001, -140.63422) |
|
107 |
(42.61234, -5.61234) |
|
108 |
(5.00001, -140.63422) |
圖 8
圖8的數(shù)據(jù)發(fā)送到服務(wù)器經(jīng)過geohash計算得出下面的hash表:
|
mid |
坐標(biāo) |
|
100 |
ezs42m34yfp_100 |
|
101 |
mh7uy8r5n6j_101 |
|
102 |
t3b9tbuu84u_102 |
|
103 |
c2b26bnk32b_103 |
|
104 |
80021bgp45m_104 |
|
105 |
k484ntdc58w_105 |
|
106 |
8bgury1r1jm_106 |
|
107 |
ezs42m34ygz_107 |
|
108 |
8bgury1r1jm_108 |
圖 9
計算出這些hash值,將hash值存入redis中,存入redis中之后,那么問題來了,如何去獲取一個用戶附近的用戶呢?當(dāng)redis數(shù)據(jù)庫中有了一些用戶的記錄之后,來一個用戶,我們先對其進行編碼,然后根據(jù)該用戶的位置hash從redis中選出該用戶附近的hash,選取附近的hash這一步很簡單,對于redis只需這么做:
<?php
$mid = 2014;
$level = 7; //獲取的精度等級,數(shù)字越大,附近這個范圍越小
$redis = Redis::init(); //假設(shè)這樣獲取到redis實例
$mykey = '8gur95yjmz'; //假設(shè)我的hash為這個
$redis->setex($mykey.'_'.$mid,$_SERVER['REQUEST_TIME'],86400); //這里設(shè)置緩存1天,具體情況具體對待
$search = substr($makey,0,$level);
$nearbys = $redis->keys("{$search}*");
?>
程序 1
上面的幾句代碼就可以選出我附近的人的hash,當(dāng)然,其中的level來設(shè)置精度的,這個數(shù)字越大,附近的人范圍越小,具體參考圖10中的值,這個表中的值是從我的導(dǎo)師李偉(weickly)那里獲取到的。要注意,這個地方搜索完之后要排除自己。還有一點要注意,就是在緩存時鍵名的最后一定要加上_{$mid},這樣做可以避免多個用戶在同一位置是互相覆蓋的情況(就像圖9中mid為106和108的用戶),放在最后是為了不影響搜索。
|
1 |
2500000m |
|
2 |
630000m |
|
3 |
78000m |
|
4 |
20000m |
|
5 |
2400m |
|
6 |
610m |
|
7 |
76m |
|
8 |
19m |
|
9 |
2m |
圖 10
例如mid為109,經(jīng)緯度為(42.61236, -5.61234)的用戶,當(dāng)他點擊獲取附近的人按鈕式,我獲取到他的經(jīng)緯度并計算出他的hash,$mykey='ezs42m34yzx',然后通過程序段1可以獲取到他附近的hash:
|
100 |
ezs42m34yfp_100 |
(42.61233,-5.61234) |
3.3m |
|
107 |
ezs42m34ygz_107 |
(42.61234, -5.61234) |
2.2m |
|
109 |
ezs42m34yzx_109 |
(42.61236, -5.61234) |
0m |
圖 11
圖11中獲取到了用戶109附近的用戶hashs,獲取到hash值還并沒有完成,首先排除掉自己109那條記錄,然后通過Geohash類中的decode將hash解碼為經(jīng)緯度,通過每個用戶的經(jīng)緯度計算出和109用戶的距離,然后按距離等級返回,比如說小于100,小于200……
至此,獲取附近的人就完成了,當(dāng)然了具體實踐的時候還要隨機應(yīng)變具體情況具體對待,我寫這篇文章只是想起到拋磚引玉的效果,本文中可能會存在很多不足,還望斧正。
本文版權(quán)歸作者(luluyrt@163.com)和博客園共有,未經(jīng)作者本人同意禁止任何形式的轉(zhuǎn)載,轉(zhuǎn)載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責(zé)任的權(quán)利。
總結(jié)
以上是生活随笔為你收集整理的lbs(查看附近的人),看看社交软件如何实现查看附近的人的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GO的下载和环境配置,Goland编译器
- 下一篇: Asp.net 项目部署的403问题