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; /**
*/ private Bitmap mStartBitmap; /**
*/ private Paint mLinePaint; /**
*/ private Paint mLightBallPaint; /**
*/ private Bitmap mLightBallBitmap; /**
*/ private Rect mStartRect; /**
*/ private int mWidth; /**
*/ private int mHeight; /**
*/ 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();
} /**
*/ 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) {
|