GPS軌跡抽侠崖:Douglas-Peuker算法

“其實(shí)卷要,人間路是一條向死而生的路,每個(gè)人的生命都是走向死亡的一個(gè)過程独榴。在這條路上我們無法回頭僧叉,也終會(huì)走到終點(diǎn)。沒有永遠(yuǎn)棺榔,只有永別瓶堕。這一路上你的所作所為,你所經(jīng)歷的事務(wù)症歇,見過的風(fēng)景同伴郎笆,便是你這一行的意義所在谭梗。我們都是彼此路上的意義⊥痱荆”


背景

在對(duì)行車軌跡在地圖上進(jìn)行展示時(shí)激捏,往往需要繪制大量的坐標(biāo)點(diǎn),但是設(shè)備取點(diǎn)的間隔往往是固定的凄吏,傳統(tǒng)的做法是將所有的點(diǎn)繪制出來缩幸,極大地消耗了瀏覽器的性能。然而在一條軌跡上竞思,兩點(diǎn)便可以確定一條直線,因此在行駛路徑比較直的區(qū)域钞护,很多坐標(biāo)點(diǎn)都是可以舍棄的盖喷,同時(shí),當(dāng)對(duì)地圖進(jìn)行縮小难咕,使可視區(qū)域變大時(shí)课梳,坐標(biāo)點(diǎn)也不必繪制地十分精確,往往取一些關(guān)鍵點(diǎn)就能繪制出行駛軌跡余佃。Douglas-Peuker正是一個(gè)對(duì)坐標(biāo)關(guān)鍵點(diǎn)進(jìn)行抽取的算法暮刃。


算法原理

1. 將軌跡的首尾點(diǎn)連成一條線,計(jì)算曲線上每一個(gè)點(diǎn)到直線的距離爆土,找出最大距離dmax椭懊,看距離是否小于給定的閾值max(及軌跡精確度)。

01.png

2. a.若dmax < max步势,則舍棄這條曲線上的所有中間點(diǎn)氧猬,將曲線首尾點(diǎn)連成的直線作為這段曲線的近似,這條線段便處理完畢坏瘩。

b.若dmax >= max盅抚,則以dmax所在點(diǎn)作為分割點(diǎn),將原來的曲線分為兩段倔矾,分出來的兩條曲線繼續(xù)直線步驟1妄均、2,直到所有的dmax < max哪自。

02.png

03.png
04.png
05.png
06.png

顯然丰包,算法的抽稀精讀是和閾值相關(guān)的,閾值越大提陶,舍棄的點(diǎn)越多烫沙,提取的關(guān)鍵點(diǎn)越少。若繪制的軌跡較長(zhǎng)隙笆,可在對(duì)地圖進(jìn)行縮放時(shí)動(dòng)態(tài)調(diào)整閾值锌蓄。


代碼實(shí)現(xiàn)

/**
 * gps軌跡抽稀算法
 * @author: Z1hgq
 */
/**
 * 點(diǎn)到直線的距離升筏,直線方程為 ax + by + c = 0
 * @param {Number} a 直線參數(shù)a
 * @param {Number} b 直線參數(shù)b
 * @param {Number} c 直線參數(shù)c
 * @param {Object} xy 點(diǎn)坐標(biāo)例如{ lat: 2, lng: 2 }
 * @return {Number}
 */
function getDistanceFromPointToLine(a, b, c, xy) {
    const x = xy.lng;
    const y = xy.lat;
    return Math.abs((a * x + b * y + c) / Math.sqrt(a * a + b * b));
}

/**
 * 根據(jù)兩個(gè)點(diǎn)求直線方程 ax+by+c=0
 * @param {Object} xy1 點(diǎn)1,例如{ lat: 1, lng: 1 }
 * @param {Object} xy2 點(diǎn)2,例如{ lat: 2, lng: 2 }
 * @return {Array} [a,b,c] 直線方程的三個(gè)參數(shù)
 */
function getLineByPoint(xy1, xy2) {
    const x1 = xy1.lng; // 第一個(gè)點(diǎn)的經(jīng)度
    const y1 = xy1.lat; // 第一個(gè)點(diǎn)的緯度
    const x2 = xy2.lng; // 第二個(gè)點(diǎn)的經(jīng)度
    const y2 = xy2.lat; // 第二個(gè)點(diǎn)的緯度

    const a = y2 - y1;
    const b = x1 - x2;
    const c = (y1 - y2) * x1 - y1 * (x1 - x2);

    return [a, b, c];
}
/**
 * 稀疏點(diǎn)
 * @param {Array} points 參數(shù)為[{lat: 1, lng: 2},{lat: 3, lng: 4}]點(diǎn)集
 * @param {Number} max 閾值,越大稀疏效果越好但是細(xì)節(jié)越差
 * @return {Array} 稀疏后的點(diǎn)集[{lat: 1, lng: 2},{lat: 3, lng: 4}],保持和輸入點(diǎn)集的順序一致
 */
function sparsePoints(points, max) {
    if (points.length < 3) {
        return points;
    }
    const xy1 = points[0]; // 取第一個(gè)點(diǎn)
    const xy2 = points[points.length - 1]; // 取最后一個(gè)點(diǎn)
    const [a, b, c] = getLineByPoint(xy1, xy2); // 獲取直線方程的a,b,c值

    let ret = []; // 最后稀疏以后的點(diǎn)集
    let dmax = 0; // 記錄點(diǎn)到直線的最大距離
    let split = 0; // 分割位置
    for (let i = 1; i < points.length - 1; i++) {
        const d = getDistanceFromPointToLine(a, b, c, points[i]);
        if (d > dmax) {
            split = i;
            dmax = d;
        }
    }

    if (dmax > max) {
        // 如果存在點(diǎn)到首位點(diǎn)連成直線的距離大于max的,即需要再次劃分
        // 按split分成左邊一份瘸爽,遞歸
        const child1 = sparsePoints(points.slice(0, split + 1), max);
        // 按split分成右邊一份您访,遞歸
        const child2 = sparsePoints(points.slice(split), max);
        // 因?yàn)閏hild1的最后一個(gè)點(diǎn)和child2的第一個(gè)點(diǎn),肯定是同一個(gè)(即為分割點(diǎn)),合并的時(shí)候,需要注意一下
        ret = ret.concat(child1, child2.slice(1));
        return ret;
    } else {
        // 如果不存在點(diǎn)到直線的距離大于閾值的,那么就直接是首尾點(diǎn)了
        return [points[0], points[points.length - 1]];
    }
}

export default sparsePoints;

效果展示

數(shù)據(jù)點(diǎn)類型為類似30.664147,104.067232,抽稀閾值為0.000005剪决,第一張圖為原始gps數(shù)據(jù)灵汪,共265個(gè)點(diǎn),第二張圖為抽稀之后的數(shù)據(jù)柑潦,共108個(gè)點(diǎn)享言。

08.png

07.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渗鬼,隨后出現(xiàn)的幾起案子览露,更是在濱河造成了極大的恐慌,老刑警劉巖譬胎,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件差牛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堰乔,警方通過查閱死者的電腦和手機(jī)偏化,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镐侯,“玉大人侦讨,你說我怎么就攤上這事∥瞿酰” “怎么了搭伤?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袜瞬。 經(jīng)常有香客問我怜俐,道長(zhǎng),這世上最難降的妖魔是什么邓尤? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任拍鲤,我火速辦了婚禮,結(jié)果婚禮上汞扎,老公的妹妹穿的比我還像新娘季稳。我一直安慰自己,他們只是感情好澈魄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布景鼠。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铛漓。 梳的紋絲不亂的頭發(fā)上溯香,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音浓恶,去河邊找鬼玫坛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛包晰,可吹牛的內(nèi)容都是我干的湿镀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伐憾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勉痴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起树肃,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蚀腿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扫外,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廓脆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年筛谚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片停忿。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驾讲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出席赂,到底是詐尸還是另有隱情吮铭,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布颅停,位于F島的核電站谓晌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏癞揉。R本人自食惡果不足惜纸肉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喊熟。 院中可真熱鬧柏肪,春花似錦、人聲如沸芥牌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)壁拉。三九已至谬俄,卻和暖如春柏靶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凤瘦。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工宿礁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蔬芥。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓梆靖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親笔诵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子返吻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容