摘要:輾轉(zhuǎn)流傳出班車手冊(cè)后發(fā)現(xiàn)搜索實(shí)在是太不方便了,于是有了一個(gè)主義,想做一個(gè)可以搜索房子地址,找出附近班車點(diǎn)類似大眾點(diǎn)評(píng)的定位搜索附近餐館的功能。
起因
七月份要去某廠報(bào)道了,異地租房的時(shí)候發(fā)現(xiàn)想租一個(gè)有公司班車的地方,卻不知道哪里有班車。輾轉(zhuǎn)流傳出班車手冊(cè)后發(fā)現(xiàn)搜索實(shí)在是太不方便了,于是有了一個(gè)主義,想做一個(gè)可以搜索房子地址,找出附近班車點(diǎn)(類似大眾點(diǎn)評(píng)的定位搜索附近餐館的功能)。現(xiàn)在做的差不多了,發(fā)現(xiàn)好像本來公司就有做這個(gè)東西。。權(quán)當(dāng)學(xué)一下一些位置匹配的技術(shù)了。
最后成果是這樣子的:
大頭針是輸入的位置(福田中學(xué)),附近的藍(lán)點(diǎn)就是一個(gè)一個(gè)站點(diǎn)。由于一個(gè)站點(diǎn)他會(huì)在上班下班夜班不同的線路的不同站點(diǎn)位置,會(huì)在不同時(shí)刻到達(dá),因此聚合為多個(gè)同一站點(diǎn)的數(shù)據(jù)會(huì)聚合為一個(gè)點(diǎn)。點(diǎn)擊藍(lán)色的站點(diǎn)就會(huì)在下面顯示出這個(gè)站點(diǎn)所在的所有線路。
具體實(shí)現(xiàn)下面將分為幾個(gè)步驟講一下具體使用了什么方法什么技術(shù):
1. 原始數(shù)據(jù)轉(zhuǎn)換成我們需要的數(shù)據(jù)一開始拿到的是excel手冊(cè),所以我們有的原始數(shù)據(jù)是長成這樣的(忽略的從excel中導(dǎo)出的步驟):
["A(B門口)(07:30)→C(政府前100米天橋下)(07:45)→D(2站臺(tái)前10米)→E→F(09:12)", ...路線二, ...路線三]
然后我們需要做的事情是:
從數(shù)組里把每一條線路的站點(diǎn)拆分成一個(gè)個(gè)獨(dú)立的單元
這一步比較簡單,str.split("→")
每一個(gè)單元分離出站點(diǎn)和時(shí)間
這一步要做的就多一點(diǎn)點(diǎn)了,需要用到正則匹配,而且因?yàn)檎军c(diǎn)的名字其實(shí)是有多種的,需要考慮到多種情況。因此我的方法是:
先用/(.*)(([0-9:]*))/分離時(shí)間和站點(diǎn),因?yàn)橹挥袝r(shí)間是左右括號(hào)內(nèi)只包含數(shù)字和:的。
實(shí)際上站點(diǎn)名稱里有一些非法字符,因此還需要進(jìn)行一步過濾station.replace(/([^u4e00-u9fa5()d])/g, "")
每一個(gè)站點(diǎn)獲取到經(jīng)緯度
這個(gè)就沒啥好說的了。。調(diào)用騰訊地圖的api,不過由于調(diào)用api有每秒請(qǐng)求數(shù)和每日請(qǐng)求數(shù)的限制,用異步回調(diào)加定時(shí)器的方式模擬了休眠,然后運(yùn)行腳本慢慢等結(jié)果返回就好了。
我參考的資料
簡單介紹一下geohash就是,把經(jīng)緯度按照一定的規(guī)則去映射出一個(gè)hash字符串,在后續(xù)搜索的時(shí)候,只要hash字符串匹配程度足夠高就可以認(rèn)為這兩個(gè)點(diǎn)是相近的。具體的內(nèi)容可以閱讀上面的參考資料。下面給我javascript代碼的實(shí)現(xiàn)。
function geoHashCode (num, range) { range = [-range, range] let retCode = [] for (let i = 1; i <= 20; i++) { let middle = (range[0] + range[1]) / 2 let code = num < middle ? "0" : "1" if (code === "0") { range[1] = middle } else { range[0] = middle } retCode.push(code) } return retCode } function geoHash ({ lng, lat }) { // lng: 經(jīng)度, lat: 緯度 let lngCode = geoHashCode(lng, 180) let latCode = geoHashCode(lat, 90) // 偶數(shù)位放經(jīng)度,奇數(shù)位放緯度,把2串編碼組合生成新串 let code = [] for (let i = 0; i < 40; i++) { if (i % 2 === 0) { // 偶數(shù) code[i] = lngCode[i / 2] } else { code[i] = latCode[(i - 1) / 2] } } const base32 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "b", "c", "d", "e", "f", "g", "h", "j", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] let newCode = [] const splitLen = 5 for (let i = 0; i < 8; i++) { newCode.push(code.slice(i * 5, i * 5 + 5).join("")) } // base32編碼 newCode = newCode.map(item => base32[parseInt(item, 2)]).join("") return newCode }
經(jīng)過上述步驟,我們可以得到什么呢?
一個(gè)很大的list,每一個(gè)單元為
{ station:班車名字, location:該點(diǎn)的經(jīng)緯度, name:屬于上班下班夜班中的哪一個(gè), lineIndex:屬于該班車類型的拿一條線路, stationIndex:屬于該線路里的第幾個(gè)站點(diǎn), time:到站時(shí)間, geohash:該點(diǎn)經(jīng)緯度映射出的的geohash }
到這一步其實(shí)已經(jīng)可以做到輸入一個(gè)點(diǎn),匹配出附近班車的點(diǎn)了,只要把輸入的點(diǎn)通過api查詢出經(jīng)緯度,再轉(zhuǎn)化成geohash,最后遍歷這個(gè)list把匹配程度足夠高的點(diǎn)挑出來就可以了。
但是其實(shí)我們有5000個(gè)這樣的點(diǎn),在頁面上不斷做這種遍歷匹配我覺得挺蠢的,于是我想到構(gòu)建一個(gè)匹配樹。把一組hash映射成一個(gè)匹配森林,然后輸入點(diǎn)的geohash不斷尋找匹配節(jié)點(diǎn)去遍歷這個(gè)森林的時(shí)候可以完全避開不匹配的項(xiàng)去提高匹配效率。舉個(gè)例子就是:
我們根據(jù)左側(cè)的hashList映射出右側(cè)的匹配森林,由于geohash的精度關(guān)系是會(huì)出現(xiàn)多個(gè)站點(diǎn)的geohash是一樣的。因此我在葉子節(jié)點(diǎn)里用一個(gè)數(shù)組存放所有的對(duì)應(yīng)站點(diǎn)信息。當(dāng)我們要匹配"wsc2"時(shí)我們可以一直搜索到葉子節(jié)點(diǎn),取出‘站點(diǎn)1,站點(diǎn)2’,但是有時(shí)候我們要搜索的geohash沒辦法匹配到葉子節(jié)點(diǎn),我們就要先判斷當(dāng)前精度是否足夠高,誤差會(huì)不會(huì)太大,比如我們認(rèn)為匹配了三個(gè)前綴字符的時(shí)候精度就足夠高了,那么搜索"ws11"的時(shí)候由于只匹配到兩個(gè),不應(yīng)返回結(jié)果。而匹配"wsc3"的時(shí)候,可以匹配到前綴字符"wsc",雖然沒有到葉子節(jié)點(diǎn),但是我們可以認(rèn)為以"wsc"為根(大概是那個(gè)意思你們應(yīng)該明白)的樹的所有葉子節(jié)點(diǎn)都可以認(rèn)為是這個(gè)geohash的附近節(jié)點(diǎn),也就是返回"站點(diǎn)1,站點(diǎn)2,站點(diǎn)6"。至于誤差范圍可以看上面的參考文獻(xiàn)。
3.構(gòu)建頁面需要的內(nèi)容騰訊地圖或者其他地圖的開放接口
獲取輸入地址轉(zhuǎn)化為經(jīng)緯度和geohash
查找樹獲取匹配的地址在list中的index
聚合相同經(jīng)緯度的點(diǎn)為一個(gè)繪制點(diǎn)
將經(jīng)緯度作為鍵名構(gòu)建一個(gè)map
繪制,附近的點(diǎn)為藍(lán)色,輸入的點(diǎn)為大頭針,綁定附近的點(diǎn)的點(diǎn)擊事件(渲染列表,生成該點(diǎn)的所有線路信息)
其他這個(gè)小玩具就這么結(jié)束了,中間其實(shí)還有一些值得一提的地方。我也就一起記下來了,感覺還是挺有趣的,做一些好玩的東西。
定時(shí)器+異步模擬休眠必備知識(shí)點(diǎn): sync/await(只是因?yàn)檫@么寫看起來很爽,沒有別的意思
function sleep () { return new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 500) }) } (async function () { let i = locationList.length // 計(jì)數(shù)器 let newList = [] while (i !== -1) { let item = locationList.pop() // 取出要查詢的點(diǎn) let location try { if (locationMap[item.station]) { // 如果這個(gè)點(diǎn)請(qǐng)求過了就直接用緩存信息 location = locationMap[item.station] } else { location = await getXY(item.station) // 調(diào)用api獲取經(jīng)緯度 locationMap[item.tation] = location // 緩存經(jīng)緯度信息 await sleep() // 休眠 } item.location = location item.geoHash = geoHash(location) // 獲取geohash newList.push(item) } catch (e) { // 請(qǐng)求失敗了,把這個(gè)點(diǎn)推回去重新請(qǐng)求 console.log(e) locationList.push(item) i++ } i-- console.log(i) } })()數(shù)據(jù)劫持
其實(shí)一開始設(shè)計(jì)的時(shí)候沒有查詢地點(diǎn)附近的班車站點(diǎn)功能的。而是顯示上班線路下班線路的功能。不同線路之間的轉(zhuǎn)換用了數(shù)據(jù)劫持的方式,也就是vue實(shí)現(xiàn)數(shù)據(jù)綁定的Object.defineProperty,還真的挺有意思的,建議大家也可以用這個(gè)試一下。另外還有單頁應(yīng)用路由里面的hashchange事件。這些都是些可以再創(chuàng)造的api。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95243.html
摘要:高德地圖入門五搜索服務(wù)搜索服務(wù)名稱說明是否插件輸入提示,根據(jù)輸入關(guān)鍵字提示匹配信息是地點(diǎn)搜索服務(wù)插件,提供某一特定地區(qū)的位置查詢服務(wù)是麻點(diǎn)圖插件,提供海量搜索結(jié)果的輔助顯示功能是行政區(qū)查詢服務(wù),提供行政區(qū)相關(guān)信息是公交路線服務(wù),提供公交路線 高德地圖 Javascript API 入門(五) 搜索服務(wù) 搜索服務(wù) 名稱 說明 是否插件 AMap.Autocomplete 輸入提示...
摘要:下面是用于,和的晶圓驅(qū)動(dòng)工具升級(jí)至這是一款非常棒的上位機(jī)驅(qū)動(dòng)開發(fā)工具,特別是基于的上位機(jī)開發(fā)。嵌入式專題教程第期基于模擬器的上位機(jī)開發(fā),僅需即可,簡單易實(shí)現(xiàn)?;ㄊ叫愀鞣N立方體的效果展示。 ?【3-5分鐘閱讀】 目錄 【EOS/ESD聯(lián)盟】 【蘇州晶湛半導(dǎo)體展示300mm工藝的1200V G...
摘要:離職新路線年的總結(jié)在這里年總結(jié),其實(shí)在發(fā)布這個(gè)文章之前,就已經(jīng)跟阿里那邊再談新的,會(huì)以的級(jí)別入職阿里閑魚部門??偹苤?,我司在月份調(diào)整了一次架構(gòu),具體如下美團(tuán)調(diào)整了組織架構(gòu)。 17年的總結(jié)來的更晚一點(diǎn),其實(shí)是一直在猶豫要不要寫,主要感覺去年一年折騰的有點(diǎn)兇殘,連續(xù)換工作以及地點(diǎn),一路走來有糾結(jié),有痛苦,有快樂,有興奮,有迷茫,有得有失,所以想了很久,還是來記錄下這一年的關(guān)鍵點(diǎn)。 離職 ...
摘要:離職新路線年的總結(jié)在這里年總結(jié),其實(shí)在發(fā)布這個(gè)文章之前,就已經(jīng)跟阿里那邊再談新的,會(huì)以的級(jí)別入職阿里閑魚部門??偹苤?,我司在月份調(diào)整了一次架構(gòu),具體如下美團(tuán)調(diào)整了組織架構(gòu)。 17年的總結(jié)來的更晚一點(diǎn),其實(shí)是一直在猶豫要不要寫,主要感覺去年一年折騰的有點(diǎn)兇殘,連續(xù)換工作以及地點(diǎn),一路走來有糾結(jié),有痛苦,有快樂,有興奮,有迷茫,有得有失,所以想了很久,還是來記錄下這一年的關(guān)鍵點(diǎn)。 離職 ...
閱讀 2873·2021-09-22 15:43
閱讀 4808·2021-09-06 15:02
閱讀 861·2019-08-29 13:55
閱讀 1695·2019-08-29 12:58
閱讀 3085·2019-08-29 12:38
閱讀 1263·2019-08-26 12:20
閱讀 2279·2019-08-26 12:12
閱讀 3329·2019-08-23 18:35