成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從拿到班車手冊(cè).xls到搜索附近班車地點(diǎn)

jhhfft / 1576人閱讀

摘要:輾轉(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é)果返回就好了。

2. 怎么在一堆經(jīng)緯度表示的點(diǎn)里找出附近的點(diǎn)呢(geohash)

我參考的資料
簡單介紹一下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

相關(guān)文章

  • 高德地圖 Javascript API 入門(五)

    摘要:高德地圖入門五搜索服務(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 輸入提示...

    yzzz 評(píng)論0 收藏0
  • 嵌入式新聞早班車-第27期

    摘要:下面是用于,和的晶圓驅(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...

    BDEEFE 評(píng)論0 收藏0
  • 2017--年度個(gè)人總結(jié)

    摘要:離職新路線年的總結(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)。 離職 ...

    2450184176 評(píng)論0 收藏0
  • 2017--年度個(gè)人總結(jié)

    摘要:離職新路線年的總結(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)。 離職 ...

    TIGERB 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

jhhfft

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<