lbs(查看附近的人),看看社交软件如何实现查看附近的人
原文?https://www.cnblogs.com/iforever/p/4061095.html?utm_source=tuicool&utm_medium=referral
? ? 最近在做一款移動端棋牌游戲,為了進(jìn)一步提高用戶體驗(yàn)、拉近玩家的距離,我們決定在游戲中加入好友功能,而對于體驗(yàn)好友功能的玩家來說,要是玩牌的時候可以看看附近都有誰在玩牌,跟他們交流交流玩牌心得什么的無疑是個不錯的想法。而要實(shí)現(xiàn)查看附近的人就需要提提LBS(Location Based Service),他的意思就是基于位置的服務(wù),就是通過移動終端獲取到許多用戶或者物體的經(jīng)緯度坐標(biāo),通過這些位置信息所提供的服務(wù)。
?????? 好了,扯了這么多,我們來看看如何實(shí)現(xiàn)查看附近人的功能的:
?????? 首先要具備下面這些環(huán)境:
?????? 好了,等這些環(huán)境都具備了之后,我來講講這個實(shí)現(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ū)間為中值到最大值(或者最小值到中值),這樣一直比較下去,直到到達(dá)要求的精度,精度和緯度的方法是一樣的,只不過一個原始區(qū)間是(-90,90),一個是(-180,180),光說不好理解,下面我們看看一個簡單的例子:
對經(jīng)度32.165進(jìn)行編碼:???????????????????????????????????????????????????????????????????????????????????????????????? 對緯度89.156進(jìn)行編碼:
| 編碼 | 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í)奇偶數(shù)位互換也可以的),對于上面的經(jīng)緯度編碼后再組碼如下:
| 經(jīng)度:10101 11101 | 緯度:10111 11101 |
| 位置編碼:11001 11011 11111 10011 |
圖 3
?????? 對于上面的位置編碼,為什么要這么編碼呢,為什么要奇數(shù)位放緯度,偶數(shù)為方經(jīng)度呢,我們看看下面這張圖,用這張圖模擬地圖的經(jīng)緯度,A點(diǎn)(-180,90),B點(diǎn)(180,90),C點(diǎn)(-180,-90),D點(diǎn)(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)分為很多很多多的小塊,每一個小塊都有唯一的二進(jìn)制編碼,當(dāng)位數(shù)達(dá)到一定的長度時就可以表示很小的一塊區(qū)域,這不就可以根據(jù)二進(jìn)制編碼定位一個唯一的位置了嗎,對于劃分的進(jìn)一步理解可看下面的圖。
圖 5
????? 如圖5所示,左邊是是對緯度(-180,180)的劃分,可以看出通過劃分可以確定(22.4,45)這一緯度區(qū)間的編碼為1001,當(dāng)然了位數(shù)越多精度越高,同理對經(jīng)度進(jìn)行劃分,可以確定(-78.75,-67.5)這一經(jīng)度范圍的編碼為0001,可以想象,當(dāng)左右兩張圖合在一起時就可以確定一個唯一的矩形區(qū)域,當(dāng)該區(qū)域足夠小的時候就可一看做一個點(diǎn)。
?
??? ? 第二步.? 組碼
????? 從圖3可以看出我們對經(jīng)緯度編碼后可得二進(jìn)制字符串11001 11011 11111 10011
????? 最后使用用0-9、b-z(去掉a, i, l, o)這32個字母進(jìn)行base32編碼,首先將11001 11011 11111 10011轉(zhuǎn)成十進(jìn)制,對應(yīng)著25、27、32、19,十進(jìn)制對應(yīng)的編碼就是tvzm。同理,將編碼轉(zhuǎn)換成經(jīng)緯度的解碼算法與之相反,具體不再贅述。至此,我們的對geohash有了個大致的了解。
??????? 圖 6
-
如何具體的應(yīng)用到程序中
?????? 首先思考一下查看附近的人的流程:
用戶點(diǎn)擊查看附近的人按鈕,首先獲取到該用戶的選位置信息(經(jīng)緯度),傳給服務(wù)器。
服務(wù)器收到數(shù)據(jù)之后對該用戶的位置信息進(jìn)行g(shù)eohash計(jì)算,獲得該用戶的位置hash字符串。
對該用戶的位置信息hash串進(jìn)行緩存(緩存時間長短根據(jù)具體情況而定)。
根據(jù)該hash串選出附近的人。
對hash進(jìn)行解碼,計(jì)算出附近用戶的位置,返回給用戶。
?????? 首先看看geohash.class.php這個公共類庫里面的基本方法:
| ? | [public]Geohash()?????? 初始化hash映射表 |
| Geohash | [public]encode(lat,lat, long)?????? 對經(jīng)緯度進(jìn)行編碼 |
| ? | [public]decode()?????? 對hash進(jìn)行解碼 |
圖 7
????? 如圖7所示,顯而易見這個類庫里面有3個函數(shù),第一個用來初始化hash映射表,其實(shí)就是把0123456789bcdefghjkmnpqrstuvwxyz字符串中的每個字符和它對應(yīng)的二進(jìn)制編碼對應(yīng)起來(左邊補(bǔ)零至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計(jì)算得出下面的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
??????? 計(jì)算出這些hash值,將hash值存入redis中,存入redis中之后,那么問題來了,如何去獲取一個用戶附近的用戶呢?當(dāng)redis數(shù)據(jù)庫中有了一些用戶的記錄之后,來一個用戶,我們先對其進(jìn)行編碼,然后根據(jù)該用戶的位置hash從redis中選出該用戶附近的hash,選取附近的hash這一步很簡單,對于redis只需這么做:
<?php$mid = 2014;$level = 7; //獲取的精度等級,數(shù)字越大,附近這個范圍越小$redis = Redis::init(); //假設(shè)這樣獲取到redis實(shí)例$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)那里獲取到的。要注意,這個地方搜索完之后要排除自己。還有一點(diǎn)要注意,就是在緩存時鍵名的最后一定要加上_{$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)他點(diǎn)擊獲取附近的人按鈕式,我獲取到他的經(jīng)緯度并計(jì)算出他的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)緯度計(jì)算出和109用戶的距離,然后按距離等級返回,比如說小于100,小于200……
??????? 至此,獲取附近的人就完成了,當(dāng)然了具體實(shí)踐的時候還要隨機(jī)應(yīng)變具體情況具體對待,我寫這篇文章只是想起到拋磚引玉的效果,本文中可能會存在很多不足,還望斧正。
?
本文版權(quán)歸作者(luluyrt@163.com)和博客園共有,未經(jīng)作者本人同意禁止任何形式的轉(zhuǎn)載,轉(zhuǎn)載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責(zé)任的權(quán)利。?
總結(jié)
以上是生活随笔為你收集整理的lbs(查看附近的人),看看社交软件如何实现查看附近的人的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从源码分析Redis分布式锁的原子性保证
- 下一篇: uwp版的音乐播放器练手