道格拉斯算法简介
道格拉斯-普克算法(Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法、迭代适应点算法、分裂与合并算法)是将曲线近似表示为一系列点,并减少点的数量的一种算法。它的优点是具有平移和旋转不变性,给定曲线与阈值后,抽样结果一定。
算法思想
算法的基本思路是:对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax ,用dmax与限差D相比:若dmax <D,这条曲线上的中间点全部舍去;若dmax ≥D,保留dmax 对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法。
应用场景
最近有需求是 页面展示某物体的运动轨迹,但是在数据库中存入了这个物体的运动轨迹,但是存入的点太多,有上万个,担心前段地图展示会比较卡。所以需要java 后端 来对运动轨迹的点来做一下抽稀。此时就比较适合考虑道格拉斯算法来对轨迹的点进行处理了.
代码实现
//地图点实体类 get,set方法 以及其他不重要的字段已省略.
public class Point{
private double lng;
private double lat;
}
//douglas算法实现类
public class DouglasUtils{
//定义允许点的最大偏差距离
public static final double max = 1f;
//利用三角形的勾股定理来求两点的距离
public static double calDistance(Point p1,Point p2){
double a = Math.round( (p2.getLng()-p1.getLng() )*100000);//求经度方向的距离差,同一纬度,经度每差一度,距离差大约 110000米
double b = Math.round( (p2.getLat()-p1.getLat() )*111320);//求纬度方向的距离差,同一经度,纬度每差一度,距离大约差100000米
return Math.sqrt(Math.pow(a,2) + Math.pow(b,2) );
}
//计算点到直线的距离,将点和线的起点终点来看成为三角形,求点到直线的距离,就是求点到这条线的高, 来利用海伦公式利用三边长来求出面积,在利用面积和底边长来求出高
public static double calPointToLine(Point start,Point end,Point target){
//求三边长
double a = calDistance(target,start);
double b = calDistance(target,end);
double c = calDistance(start,end);
double p = (a + b + c)/2.0;
//求出三角形面积
double s = Math.sqrt(Math.abs( p*(p-a)*(p-b)*(p-c) ));
return s*2.0/c;
}
//递归方法实现轨迹点压缩
public static void compressLine(List<Point> points,int start,int end,List<Point> resList){
Point startPoint = points.get(start);
Point endPoint = points.get(end);
double maxDistance = 0;
int index = 0;
//遍历出离线距离最大的点
for(int i=start;i<=end;i++){
double currDistance = calPointToLine(startPoint,points.get(),points.get(i));
if(currDistance>maxDistance){
maxDistance = currDistance;
index = i;
}
}
//如果离线最大距离 没有超过了我们设置的最大偏移距离,就再以这个点为界限,分成两段,再分别去递归查询,并且要把这个点添加到存放压缩点的resList
//添加元素一定要在递归该点之前的路线之后添加,这样可以保证点的相对顺序不会变,否则点会顺序不对,在地图上连线是有问题的
if(maxDistance>max && index!=0){
compressLine(points,start,index-1,resList);
if(!resList.contains(points.get(index))){
resList.add(points.get(index));
}
compressLine(points,index+1,end-1,resList);
}
}
//获取压缩点的结果的方法,要想用道格拉斯算法抽稀点就可以调用此方法,传入点的集合即可.
public static List<Point> getCompressList(List<Point> list){
//存放压缩后点的集合
List<Point> resList = new ArrayList<>();
if(list.isEmpty()){
return resList;
}
compressLine(list,0,list.size()-1,resList);
return resList;
}
}
经纬度丢失计算距离
1.纬度相同,经度不同
在纬度相同的情况下:
经度每隔0.00001度,距离相差约1米;
每隔0.0001度,距离相差约10米;
每隔0.001度,距离相差约100米;
每隔0.01度,距离相差约1000米;
每隔0.1度,距离相差约10000米。
2.经度相同,纬度不同
纬度每隔0.00001度,距离相差约1.1米;
每隔0.0001度,距离相差约11米;
每隔0.001度,距离相差约111米;
每隔0.01度,距离相差约1113米;
每隔0.1度,距离相差约11132米。