0
点赞
收藏
分享

微信扫一扫

Android地图轨迹抽稀、动态绘制(1),写给正在求职的安卓开发

佳简诚锄 2022-01-25 阅读 48
  • @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 compress() {

int size = mLineInit.size();

ArrayList 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() {

@Override

public int compare(LatLngPoint o1, LatLngPoint o2) {

return o1.compareTo(o2);

}

});

ArrayList 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 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[current
Index]);

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

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、设置数据

/**

  • 绘制运动轨迹

  • @param mPositions 道格拉斯算法抽稀过后对应的点坐标
    map.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、设置数据

/**

  • 绘制运动轨迹

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

举报

相关推荐

0 条评论