要實現附近的人這個功能,我們要經歷以下幾個環節:
使用者定時上傳自己的定位資訊,並存到服務端的資料庫中;
使用者發起查詢請求,服務端根據使用者提供的定位資訊去資料庫中查詢與他的經度緯度海拔最接近的其他使用者的定位資訊。
服務端通過兩個定位資訊就能算出距離,按照遠近排好序後,連同對應的使用者賬戶,暱稱,頭像等資訊一同發給查詢的戶。
使用者的手機在連上gps後,每隔一定時間就會收到一定格式的資料,資料格式為:$資訊型別,x,x,x,x,x,x,x,x,x,x,x,x,x每行開頭的字元都是$,接著是資訊型別,後面是資料,以逗號分隔開。 一行完整的資料如下:
$gprmc,080655.00,a,4546.40891,n,12639.65641,e,1.045,328.42,170809,a*60
資訊型別分為以下幾種:gpgsv(可見衛星資訊),gpgll(地理定位資訊),gprmc(推薦最小定位資訊),gpvtg(地面速度資訊),gpgga(gps定位資訊),gpgsa(當前衛星資訊),下邊是對幾個主要資訊的描述。
我們最常用的是gprmc,也就是最小定位資訊,資料格式為:
$gprmc,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> utc 時間,hhmmss(時分秒)格式,這個是格林威治時間,是世界時間(utc),我們需要把它轉換成北京時間(btc),btc和utc差了8個小時,要在這個時間基礎上加8個小時;
<2> 定位狀態,a=有效定位,v=無效定位,在接收到有效資料前,這個位是『v』,後面的資料都為空,接到有效資料後,這個位是『a』,後面才開始有資料;
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸) ,我們需要把它轉換成度分秒的格式,計算方法:如接收到的緯度是:4546.40891 ,4546.40891/100=45.4640891可以直接讀出45度, 4546.40891–45*100=46.40891, 可以直接讀出46分,46.40891–46 =0.40891*60=24.5346讀出24秒, 所以緯度是:45度46分24秒;
<4> 緯度半球n(北半球)或s(南半球) ,這個位有兩種值『n』(北緯)和『s』(南緯)。
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸) ,經度的計算方法和緯度的計算方法一樣;
<6> 經度半球e(東經)或w(西經) ,這個位有兩種值『e』(東經)和『w』(西經);
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸) ,這個速率值是海浬/時,單位是節,要把它轉換成千公尺/時,根據:1海浬=1.85公里,把得到的速率乘以1.85;
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸) ,指的是偏離正北的角度;
<9> utc 日期,ddmmyy(日月年)格式 ,這個日期是準確的,不需要轉換;
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸) ;
<11> 磁偏角方向,e(東)或w(西) ;
<12>模式指示(僅nmea01833.00版本輸出,a=自主定位,d=差分,e=估算,n=資料無效) 。
gps定位資料gpgga,資料格式為:
$gpgga,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,m,<10>,m,<11>,<12>*xx<1> utc 時間,格式為hhmmss.sss;
<2> 緯度,格式為ddmm.mmmm(第一位是零也將傳送);
<3> 緯度半球,n 或s(北緯或南緯);
<4> 經度,格式為dddmm.mmmm(第一位零也將傳送);
<5> 經度半球,e 或w(東經或西經);
<6> 定位質量指示,0=定位無效,1=定位有效;
<7> 使用衛星數量,從00到12(第乙個零也將傳送);
<8> 水平精確度,0.5到99.9;
<9> 天線離海平面的高度,-9999.9到9999.9公尺m指單位公尺;
<10> 大地水準面高度,-9999.9到9999.9公尺m指單位公尺;
<11> 差分gps資料期限(rtcmsc-104),最後設立rtcm傳送的秒數量;
<12> 差分參考基站標號,從0000到1023(首位0也將傳送)。
地面速度資訊gpvtg,資料格式為:
$gpvtg,<1>,t,<2>,m,<3>,n,<4>,k,<5>*hh
<1> 以正北為參考基準的地面航向(000~359度,前面的0也將被傳輸);
<2> 以磁北為參考基準的地面航向(000~359度,前面的0也將被傳輸);
<3> 地面速率(000.0~999.9節,前面的0也將被傳輸);
<4> 地面速率(0000.0~1851.8公里/小時,前面的0也將被傳輸);
<5> 模式指示(僅nmea0183 3.00版本輸出,a=自主定位,d=差分,e=估算,n=資料無效。
有了這些定位資訊,我們就能精確的找到每個使用者現在的位置。但是由於使用者的資料量可能很大,例如有1000萬,1個億,如果拿你的定位資訊同其他所有使用者的定位資訊整個對比一次,會耗費很長的時間和計算資源,無法支援大量使用者同時使用這項功能,在這裡我們推薦使用比較容易搭建的而且可以快速上手的矩形演算法方案。
geohash是一種位址編碼方法。他能夠把二維的空間經緯度資料編碼成乙個字串,geohash用乙個字串表示經度和緯度兩個座標。在資料庫中可以實現在一列上應用索引。geohash表示的並不是乙個點,而是乙個矩形區域。geohash編碼的字首可以表示更大的區域。例如wx4g0ec1,它的字首wx4g0e表示包含編碼wx4g0ec1在內的更大範圍。 這個特性可以用於附近地點搜尋。
1、將經緯度轉換成二進位制:比如這樣乙個點(39.928167, 116.389550),地球緯度區 間是(-90,90),其中間值為0。對於緯度39.928167,在區間(0,90)中,因此得 到乙個1;(0,90)區間的中間值為45度,緯度39.923201小於45,因此得到乙個0, 依次計算下去,即可得到緯度的二進位制表示。如下圖:
根據緯度算編碼
bit min mid max
1 -90.000 0.000 90.000
0 0.000 45.000 90.000
1 0.000 22.500 45.000
1 22.500 33.750 45.000
1 33.7500 39.375 45.000
0 39.375 42.188 45.000
0 39.375 40.7815 42.188
0 39.375 40.07825 40.7815
1 39.375 39.726625 40.07825
1 39.726625 39.9024375 40.07825
2、合併緯度、經度的二進位制:通過上述計算,緯度產生的編碼為10111 00011,同理 經度產生的編碼為11010 01011。偶數字放經度,奇數字放緯度,把2串編碼組合生成 新串: 11100 11101 00100 01111。
3、按照base32進行編碼:將上述合併後二進位制編碼後結果為 「wx4g」。
4、然後就可以根據類似select * from table where geohash like 『wx4g%』;這樣的sql語句去資料庫進行模糊查詢。
5、將查詢到的字串用base32進行解碼,重新獲取到經度緯度點。
base32編碼長度與距離
geohash distance
length km
1 ±2500
2 ±630
3 ±78
4 ±20
5 ±2.4
6 ±0.61
7 ±0.076
8 ±0.019
9 ±0.00478
10 ±0.0005971
11 ±0.0001492
12 ±0.0000186
用geohash演算法的優點:1.可以保護使用者的隱私,由於geohash的每乙個字串代表了某乙個矩形區域,就是指這個矩形中的所有的點都公用乙個geohash字串,乙個geohash字串只會表示大概區域;2.可以做快取,如果使用者不斷傳送查詢附近的人的請求,那麼我們就可以把這個字串當key,把附近的人的資訊當做value儲存起來。
nodejs mongo 實現搜附近的人
參考 用mongo作為儲存,來實現搜尋附近的人具有先天的優勢,mongodb原生支援地理位置索引,可以直接用於位置距離計算和查詢。另外,它也是如今最流行的nosql資料庫之一,除了能夠很好地支援地理位置計算之外,還擁有諸如面向集合儲存 模式自由 高效能 支援複雜查詢 支援完全索引等等特性。先看一下我...
如何實現查詢附近的人
問題 給定乙個使用者a,返回與此使用者相距小於d的所有使用者。支援geo的後端儲存有mongodb,redis等。那麼如果讓我們實現,我們應該怎麼做呢?思路 圍繞此使用者生成乙個圓形,半徑是d,返回所有被此園覆蓋的使用者。方法1 先求方,再求園。如果直接求園,每乙個使用者都要計算距離值,無法利用到索...
查詢附近的人 mongodb的實現
最近做乙個關於附近的人查詢,參考了很多資料 現在市面上主流的做法 1.用資料庫 2.用類存 3.用lucene 我選用的是用mongodb自帶的查附近的人的方法,如下 dbobject db new basicdbobject cityid json.parse dbobject near new ...