Android GPS定位軌跡抽稀之道格拉斯-普克(Douglas-Peuker)算法詳解

1竖哩、抽稀

通俗點講椅贱,直接舉個栗子吧:我們知道運動軌跡實際上是由很多個經(jīng)緯度坐標(biāo)連接而成。那么我們是否需要將所有運動時記錄下來的經(jīng)緯度坐標(biāo)都用來繪制軌跡呢策严?其實是沒必要的,很多數(shù)據(jù)其實是多余的饿敲,實際上將這些多余的數(shù)據(jù)剔除仍然能保證軌跡曲線形狀大致不變妻导,而且還能讓曲線更平滑更節(jié)省存儲空間,類似這樣的過程我們就稱之為抽稀怀各。抽稀的算法很多栗竖,這里將介紹一種經(jīng)典的算法:道格拉斯-普克(Douglas-Peuker)算法。

2渠啤、道格拉斯-普克(Douglas-Peuker)算法

還是舉個栗子吧狐肢,假設(shè)在平面坐標(biāo)系上有一條由N個坐標(biāo)點組成的曲線,已設(shè)定一個閾值epsilon沥曹。
(1)首先份名,將起始點與結(jié)束點用直線連接碟联, 再找出到該直線的距離最大,同時又大于閾值epsilon的點并記錄下該點的位置(這里暫且稱其為最大閾值點)僵腺,如圖所示:

(2)接著鲤孵,以該點為分界點,將整條曲線分割成兩段(這里暫且稱之為左曲線和右曲線)辰如,將這兩段曲線想象成獨立的曲線然后重復(fù)操作(1)普监,找出兩邊的最大閾值點,如圖所示:

(3)最后琉兜,重復(fù)操作(2)(1)直至再也找不到最大閾值點為止凯正,然后將所有最大閾值點按順序連接起來便可以得到一條更簡化的,更平滑的豌蟋,與原曲線十分近似的曲線廊散,如圖所示:

2、如何實現(xiàn)梧疲?

OK允睹,終于到代碼登場了,不廢話幌氮,上代碼:
Point類:

public class Point {
    double x;
    double y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.print("(" + x + "," + y + ") ");
    }

    public static Point instance(int x, int y) {
        return new Point(x, y);
    }
}

DouglasPeuckerUtil 類:

public class DouglasPeuckerUtil {

    public static void main(String[] args) {

        System.out.print("原始坐標(biāo):");

        List<Point> points = new ArrayList<>();
        List<Point> result = new ArrayList<>();

        points.add(Point.instance(1, 1));
        points.add(Point.instance(2, 2));
        points.add(Point.instance(3, 4));
        points.add(Point.instance(4, 1));
        points.add(Point.instance(5, 0));
        points.add(Point.instance(6, 3));
        points.add(Point.instance(7, 5));
        points.add(Point.instance(8, 2));
        points.add(Point.instance(9, 1));
        points.add(Point.instance(10, 6));

        System.out.println("");
        System.out
                .println("=====================================================================");
        System.out.print("抽稀坐標(biāo):");

        result = DouglasPeucker(points, 1);

        for (Point p : result) {
            System.out.print("(" + p.x + "," + p.y + ") ");
        }
    }

    public static List<Point> DouglasPeucker(List<Point> points, int epsilon) {
        // 找到最大閾值點缭受,即操作(1)
        double maxH = 0;
        int index = 0;
        int end = points.size();
        for (int i = 1; i < end - 1; i++) {
            double h = H(points.get(i), points.get(0), points.get(end - 1));
            if (h > maxH) {
                maxH = h;
                index = i;
            }
        }

        // 如果存在最大閾值點,就進(jìn)行遞歸遍歷出所有最大閾值點
        List<Point> result = new ArrayList<>();
        if (maxH > epsilon) {
            List<Point> leftPoints = new ArrayList<>();// 左曲線
            List<Point> rightPoints = new ArrayList<>();// 右曲線
            // 分別提取出左曲線和右曲線的坐標(biāo)點
            for (int i = 0; i < end; i++) {
                if (i <= index) {
                    leftPoints.add(points.get(i));
                    if (i == index)
                        rightPoints.add(points.get(i));
                } else {
                    rightPoints.add(points.get(i));
                }
            }

            // 分別保存兩邊遍歷的結(jié)果
            List<Point> leftResult = new ArrayList<>();
            List<Point> rightResult = new ArrayList<>();
            leftResult = DouglasPeucker(leftPoints, epsilon);
            rightResult = DouglasPeucker(rightPoints, epsilon);

            // 將兩邊的結(jié)果整合
            rightResult.remove(0);//移除重復(fù)點
            leftResult.addAll(rightResult);
            result = leftResult;
        } else {// 如果不存在最大閾值點則返回當(dāng)前遍歷的子曲線的起始點
            result.add(points.get(0));
            result.add(points.get(end - 1));
        }
        return result;
    }

    /**
     * 計算點到直線的距離
     * 
     * @param p
     * @param s
     * @param e
     * @return
     */
    public static double H(Point p, Point s, Point e) {
        double AB = distance(s, e);
        double CB = distance(p, s);
        double CA = distance(p, e);

        double S = helen(CB, CA, AB);
        double H = 2 * S / AB;

        return H;
    }

    /**
     * 計算兩點之間的距離
     * 
     * @param p1
     * @param p2
     * @return
     */
    public static double distance(Point p1, Point p2) {
        double x1 = p1.x;
        double y1 = p1.y;

        double x2 = p2.x;
        double y2 = p2.y;

        double xy = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        return xy;
    }

    /**
     * 海倫公式该互,已知三邊求三角形面積
     * 
     * @param cB
     * @param cA
     * @param aB
     * @return 面積
     */
    public static double helen(double CB, double CA, double AB) {
        double p = (CB + CA + AB) / 2;
        double S = Math.sqrt(p * (p - CB) * (p - CA) * (p - AB));
        return S;
    }

輸出結(jié)果:

OK米者,平面坐標(biāo)上的Douglas-Peuker算法已經(jīng)基本實現(xiàn)了!但是如果換成經(jīng)緯度呢慢洋?其實不用擔(dān)心塘雳,地圖API一般都會提供計算兩個經(jīng)緯度坐標(biāo)之間距離的函數(shù),所以萬變不離其宗普筹,思路還是一樣的败明,大膽點,代碼啪啪啪的敲起來吧太防!

這里有一個基于百度地圖實現(xiàn)的Demo供大家參考:
https://github.com/wnn1302/TrackDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妻顶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜒车,更是在濱河造成了極大的恐慌讳嘱,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酿愧,死亡現(xiàn)場離奇詭異沥潭,居然都是意外死亡,警方通過查閱死者的電腦和手機嬉挡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門钝鸽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汇恤,“玉大人,你說我怎么就攤上這事拔恰∫蚧眩” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵颜懊,是天一觀的道長财岔。 經(jīng)常有香客問我,道長河爹,這世上最難降的妖魔是什么匠璧? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮昌抠,結(jié)果婚禮上患朱,老公的妹妹穿的比我還像新娘鲁僚。我一直安慰自己炊苫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布冰沙。 她就那樣靜靜地躺著侨艾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拓挥。 梳的紋絲不亂的頭發(fā)上唠梨,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音侥啤,去河邊找鬼当叭。 笑死,一個胖子當(dāng)著我的面吹牛盖灸,可吹牛的內(nèi)容都是我干的蚁鳖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼赁炎,長吁一口氣:“原來是場噩夢啊……” “哼醉箕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起徙垫,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤讥裤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姻报,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體己英,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年吴旋,在試婚紗的時候發(fā)現(xiàn)自己被綠了损肛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寒亥。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖荧关,靈堂內(nèi)的尸體忽然破棺而出溉奕,到底是詐尸還是另有隱情,我是刑警寧澤忍啤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布加勤,位于F島的核電站,受9級特大地震影響同波,放射性物質(zhì)發(fā)生泄漏鳄梅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一未檩、第九天 我趴在偏房一處隱蔽的房頂上張望戴尸。 院中可真熱鬧,春花似錦冤狡、人聲如沸孙蒙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挎峦。三九已至,卻和暖如春合瓢,著一層夾襖步出監(jiān)牢的瞬間坦胶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人葵第。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像纪岁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钙皮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

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