0
点赞
收藏
分享

微信扫一扫

Android地图轨迹抽稀、动态绘制

kiliwalk 2022-04-25 阅读 39
  • x

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

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

  • @param start 起始下标

  • @param end 结束下标

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

*/

private ArrayList compressLine(LatLngPoint[] originalLatLngs, ArrayList 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个点,经过抽稀之后绘制在地图上的样式。算法中传入的阙值是104417个点处理之后只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) {

int width = mStartBitmap.getWidth() / 2;

int height = mStartBitmap.getHeight() / 2;

mStartRect = new Rect();

mStartRect.left = mStartPoint.x - width;

mStartRect.right = mStartPoint.x + width;

mStartRect.top = mStartPoint.y - height;

mStartRect.bottom = mStartPoint.y + height;

}

canvas.drawBitmap(mStartBitmap, null, mStartRect, mStartPaint);

}

}

[](()3、设置数据

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》无偿开源 徽信搜索公众号【编程进阶路】

/**

  • 绘制运动轨迹

  • @param mPositions 道格拉斯算法抽稀过后对应的点坐标

  • @param startPointResId 起点图片的资源id

  • @param lightBall 小亮球的资源id

  • @param listener 轨迹绘制完成的监听

*/

public void drawSportLine(final List mPositions, @DrawableRes int startPointResId,@DrawableRes int lightBall, final OnTrailChangeListener listener) {

if (mPositions.size() <= 1) {

listener.onFinish();

return;

}

//用于

Path path = new Path();

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

if (i == 0) {

path.moveTo(mPositions.get(i).x, mPositions.get(i).y);

} else {

path.lineTo(mPositions.get(i).x, mPositions.get(i).y);

}

}

final PathMeasure pathMeasure = new PathMeasure(path, false);

//轨迹的长度

final float length = pathMeasure.getLength();

if (length < ViewUtil.dip2Px(getContext(), 16)) {

listener.onFinish();

return;

}

//动态图中展示的亮色小球(UI切图)

mLightBallBitmap = BitmapFactory.decodeResource(getResources(), lightBall);

//起点

mStartPoint = new Point(mPositions.get(0).x, mPositions.get(0).y);

mStartBitmap = BitmapFactory.decodeResource(getResources(), startPointResId);

ValueAnimator animator = ValueAnimator.ofFloat(0, length);

animator.setDuration(3000);

animator.setInterpolator(new AccelerateDecelerateInterpolator());

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float value = (Float) animation.getAnimatedValue();

// 获取当前点坐标封装到mCurrentPosition

pathMeasure.getPosTan(value, mCurrentPosition, null);

if (value == 0) {

//如果当前的运动轨迹长度为0,设置path的起点

mLinePath.moveTo(mPositions.get(0).x, mPositions.get(0).y);

}

//pathMeasure.getSegment()方法用于保存当前path路径,

//下次绘制时从上一次终点位置处绘制,不会从开始的位置开始绘制。

pathMeasure.getSegment(0, value, mLinePath, true);

invalidate();

//如果当前的长度等于pathMeasure测量的长度,则表示绘制完毕,

if (value == length && listener != null) {

listener.onFinish();

}

}

});

animator.start();

}

举报

相关推荐

0 条评论