Android 集成高德地图对轨迹抽稀以及道格拉斯-普克 算法详解

今天给大家分享一个段子:

今天我心血来潮的打开淘宝,想搜一搜有没有武功秘籍卖,没想到还真被我搜到了。 我二话不说就直接找客服询问,客服秒回道:“亲,请问看中哪一本秘籍了?我们店铺正在打折,给你一个实惠价。” 为了获得更多的折扣,我故意回道:“前些年在你家买了一本《火炼金身》,如今大成之后,一旦动用,总有种自焚的感觉。请问这是怎么回事?” 客服不愧是见过世面的人,立马回道:“亲,请你稍等一下,我这就去查一查有关于《火炼金身》的所有资料。” 十分钟后,手机响起提示声:“亲,找到了。《火炼金身》属于内经,如果硬练,必定难逃自焚之祸。亲,你现在功力太强,可不能继续练下去了。现在摆在你面前有两条路可选。一是去波斯,找教主传功给你。” 去波斯?简直是在逗我,于是我又问道:“那第二条路呢?” 客服亲切地发来了好几条链接,并说道:“第二条就是让你的身体强大到不惧灼烧感。亲,《金钟罩》和《铁布衫》了解一下,打折价四万五卖给你,要不?” “不了,我这还有本《十三太保横练》。等我将它练到满级,再来找你。” 客服:……

转入正题,在做项目的时候有一个需求是实现车辆轨迹绘制的,但是经纬的集合数据过于庞大,达到5万等多条, 我尼玛,,,,, 这么多的数据轨迹会不变形么?

于是在网上搜索了很多的文章,高德开发文档也看了好几遍, 终于功夫不负有心人。。。  在高德API平台看到了对于轨迹的处理。

一、高德API中对于轨迹变形的处理

轨迹平滑处理

原始轨迹:

           

处理后轨迹:

详细代码就不上了,直接源码地址走起:

GitHub - amap-demo/android-path-smooth: 轨迹平滑处理示例

二、道格拉斯-普克(Douglas-Peuker)算法详解:

转自: _菜鸟猿     文章地址:Android GPS定位轨迹抽稀之道格拉斯-普克(Douglas-Peuker)算法详解 - 简书

1、抽稀

通俗点讲,直接举个栗子吧:我们知道运动轨迹实际上是由很多个经纬度坐标连接而成。那么我们是否需要将所有运动时记录下来的经纬度坐标都用来绘制轨迹呢?其实是没必要的,很多数据其实是多余的,实际上将这些多余的数据剔除仍然能保证轨迹曲线形状大致不变,而且还能让曲线更平滑更节省存储空间,类似这样的过程我们就称之为抽稀。抽稀的算法很多,这里将介绍一种经典的算法:道格拉斯-普克(Douglas-Peuker)算法。

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

  还是举个栗子吧,假设在平面坐标系上有一条由N个坐标点组成的曲线,已设定一个阈值epsilon
(1)首先,将起始点与结束点用直线连接, 再找出到该直线的距离最大,同时又大于阈值epsilon的点并记录下该点的位置(这里暂且称其为最大阈值点),如图所示:

(2)接着,以该点为分界点,将整条曲线分割成两段(这里暂且称之为左曲线和右曲线),将这两段曲线想象成独立的曲线然后重复操作(1),找出两边的最大阈值点,如图所示:

(3)最后,重复操作(2)(1)直至再也找不到最大阈值点为止,然后将所有最大阈值点按顺序连接起来便可以得到一条更简化的,更平滑的,与原曲线十分近似的曲线,如图所示:

2、如何实现?

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("原始坐标:");

        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("抽稀坐标:");

        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;
            }
        }

        // 如果存在最大阈值点,就进行递归遍历出所有最大阈值点
        List<Point> result = new ArrayList<>();
        if (maxH > epsilon) {
            List<Point> leftPoints = new ArrayList<>();// 左曲线
            List<Point> rightPoints = new ArrayList<>();// 右曲线
            // 分别提取出左曲线和右曲线的坐标点
            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));
                }
            }

            // 分别保存两边遍历的结果
            List<Point> leftResult = new ArrayList<>();
            List<Point> rightResult = new ArrayList<>();
            leftResult = DouglasPeucker(leftPoints, epsilon);
            rightResult = DouglasPeucker(rightPoints, epsilon);

            // 将两边的结果整合
            rightResult.remove(0);//移除重复点
            leftResult.addAll(rightResult);
            result = leftResult;
        } else {// 如果不存在最大阈值点则返回当前遍历的子曲线的起始点
            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;
    }
}

输出结果:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值