IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android地图轨迹抽稀、动态绘制,Android架构师之路 -> 正文阅读

[移动开发]Android地图轨迹抽稀、动态绘制,Android架构师之路

 public LatLngPoint(int id,LatLng latLng){

    this.id = id;

    this.latLng = latLng;

}



@Override

public int compareTo(@NonNull LatLngPoint o) {

    if (this.id < o.id) {

        return -1;

    } else if (this.id > o.id)

        return 1;

    return 0;

}

}




使用三角形面积(使用海伦公式求得)相等方法计算点pX到点pA和pB所确定的直线的距离,  

`AMapUtils.calculateLineDistance(start.latLng, end.latLng)`计算两点之间的距离,此公式高德API



/**

 * 使用三角形面积(使用海伦公式求得)相等方法计算点pX到点pA和pB所确定的直线的距离

 * @param start  起始经纬度

 * @param end    结束经纬度

 * @param center 前两个点之间的中心点

 * @return 中心点到 start和end所在直线的距离

 */

private double distToSegment(LatLngPoint start, LatLngPoint end, LatLngPoint center) {

    double a = Math.abs(AMapUtils.calculateLineDistance(start.latLng, end.latLng));

    double b = Math.abs(AMapUtils.calculateLineDistance(start.latLng, center.latLng));

    double c = Math.abs(AMapUtils.calculateLineDistance(end.latLng, center.latLng));

    double p = (a + b + c) / 2.0;

    double s = Math.sqrt(Math.abs(p * (p - a) * (p - b) * (p - c)));

    double d = s * 2.0 / a;

    return d;

}



Douglas工具类具体代码



public Douglas(ArrayList mLineInit, double dmax) {

    if (mLineInit == null) {

        throw new IllegalArgumentException("传入的经纬度坐标list == null");

    }

    this.dMax = dmax;

    this.start = 0;

    this.end = mLineInit.size() - 1;

    for (int i = 0; i < mLineInit.size(); i++) {

        this.mLineInit.add(new LatLngPoint(i, mLineInit.get(i)));

    }

}



/**

 * 压缩经纬度点

 *

 * @return

 */

public ArrayList<LatLng> compress() {

    int size = mLineInit.size();

    ArrayList<LatLngPoint> latLngPoints = compressLine(mLineInit.toArray(new LatLngPoint[size]), mLineFilter, start, end, dMax);

    latLngPoints.add(mLineInit.get(0));

    latLngPoints.add(mLineInit.get(size-1));

    //对抽稀之后的点进行排序

    Collections.sort(latLngPoints, new Comparator<LatLngPoint>() {

        @Override

        public int compare(LatLngPoint o1, LatLngPoint o2) {

            return o1.compareTo(o2);

        }

    });

    ArrayList<LatLng> latLngs = new ArrayList<>();

    for (LatLngPoint point : latLngPoints) {

        latLngs.add(point.latLng);

    }

    return latLngs;

}



/**

 * 根据最大距离限制,采用DP方法递归的对原始轨迹进行采样,得到压缩后的轨迹

 * x

 *

 * @param originalLatLngs 原始经纬度坐标点数组

 * @param endLatLngs      保持过滤后的点坐标数组

 * @param start           起始下标

 * @param end             结束下标

 * @param dMax            预先指定好的最大距离误差

 */

private ArrayList<LatLngPoint> compressLine(LatLngPoint[] originalLatLngs, ArrayList<LatLngPoint> endLatLngs, int start, int end, double dMax) {

    if (start < end) {

        //递归进行调教筛选

        double maxDist = 0;

        int currentIndex = 0;

        for (int i = start + 1; i < end; i++) {

            double currentDist = distToSegment(originalLatLngs[start], originalLatLngs[end], originalLatLngs[i]);

            if (currentDist > maxDist) {

                maxDist = currentDist;

                currentIndex = i;

            }

        }

        //若当前最大距离大于最大距离误差

        if (maxDist >= dMax) {

            //将当前点加入到过滤数组中

            endLatLngs.add(originalLatLngs[currentIndex]);

            //将原来的线段以当前点为中心拆成两段,分别进行递归处理

            compressLine(originalLatLngs, endLatLngs, start, currentIndex, dMax);

            compressLine(originalLatLngs, endLatLngs, currentIndex, end, dMax);

        }

    }

    return endLatLngs;

}



### []( )结果:



上图中展示的轨迹是定位得到的**4417**个点,经过抽稀之后绘制在地图上的样式。算法中传入的阙值是**10**,**4417**个点处理之后只**136**个点。而且这136个点绘制的轨迹和4417个点绘制的轨迹**几乎没有什么差别。**  

不知道你们有没有被震撼到,反正我是彻彻底底被震到了。作为算法小白的我,感觉整个世界都被颠覆了。



### []( )二、轨迹绘制-自定义运动轨迹View



最开始得时候认为直接在地图上绘制动态轨迹的,根据高德提供绘制轨迹的API,结果直接卡死。当时一脸懵逼的找高德客服,一提高德的客服更让人窝火。算了不提了。后面自己试了好多遍之后放弃直接在地图上绘制,不知道哪一刻,就突然想到在地图上覆盖一个自定义的View。当时有一瞬间觉得自己是这个世界上智商最接近250的 ┐(‘~\`;)┌  

地图API提供了经纬度转换成手机上的坐标,所以可以拿到地图上点对应的屏幕的位置,也就自然可以自定义一个View动态的绘制轨迹,当自定义View的动画结束之后,隐藏自定义View然后在地图上绘制轨迹。这就是我的整体思路,下面袖子撸起,上代码。



### []( )1、初始化变量、画笔、path



  • 起点Paint

    */

    private Paint mStartPaint;

    /**

    • 起点

    */

    private Point mStartPoint;

    /**

    • 起点bitmap

    */

    private Bitmap mStartBitmap;

    /**

    • 轨迹

    */

    private Paint mLinePaint;

    /**

    • 小亮球

    */

    private Paint mLightBallPaint;

    /**

    • 小两球的bitmap UI切图

    */

    private Bitmap mLightBallBitmap;

    /**

    • 起点rect 如果为空时不绘制小亮球

    */

    private Rect mStartRect;

    /**

    • 屏幕宽度

    */

    private int mWidth;

    /**

    • 屏幕高度

    */

    private int mHeight;

    /**

    • 轨迹path

    */

    private Path mLinePath;

    /**

    • 保存每一次刷新界面轨迹的重点坐标

    */

    private float[] mCurrentPosition = new float[2];

    public SportTrailView(Context context) {

    this(context, null);
    

    }

    public SportTrailView(Context context, @Nullable AttributeSet attrs) {

    this(context, attrs, 0);
    

    }

    public SportTrailView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);
    
    initPaint();
    

    }

    /**

    • 初始化画笔,path

    */

    private void initPaint() {

    mLinePaint = new Paint();
    
    mLinePaint.setColor(Color.parseColor("#ff00ff42"));
    
    mLinePaint.setStyle(Paint.Style.STROKE);
    
    mLinePaint.setStrokeWidth(10);
    
    mLinePaint.setStrokeCap(Paint.Cap.ROUND);
    
    mLinePaint.setAntiAlias(true);
    
    
    
    mLightBallPaint = new Paint();
    
    mLightBallPaint.setAntiAlias(true);
    
    mLightBallPaint.setFilterBitmap(true);
    
    
    
    mStartPaint = new Paint();
    
    mStartPaint.setAntiAlias(true);
    
    mStartPaint.setFilterBitmap(true);
    
    
    
    mLinePath = new Path();
    

    }




### []( )2、轨迹View绘制



protected void onDraw(Canvas canvas) {

    super.onDraw(canvas);

    //绘制轨迹

    canvas.drawPath(mLinePath, mLinePaint);

    //绘制引导亮球

    if (mLightBallBitmap !=null && mStartRect !=null){

        int width = mLightBallBitmap.getWidth();

        int height = mLightBallBitmap.getHeight();

        RectF rect = new RectF();

        rect.left = mCurrentPosition[0] - width;

        rect.right = mCurrentPosition[0] + width;

        rect.top = mCurrentPosition[1] - height;

        rect.bottom = mCurrentPosition[1] + height;

        canvas.drawBitmap(mLightBallBitmap, null, rect, mLightBallPaint);

    }

    //绘制起点

    if (mStartBitmap != null && mStartPoint != null) {

        if (mStartRect == null) {
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章           查看所有文章
加:2021-09-08 10:51:10  更:2021-09-08 10:53:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:43:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码