1、算法概述
道格拉斯-普克算法(Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法、迭代适应点算法、分裂与合并算法)是将曲线近似表示为一系列点,并减少点的数量的一种算法。该算法的原始类型分别由乌尔斯·拉默(Urs Ramer)于1972年以及大卫·道格拉斯(David Douglas)和托马斯·普克(Thomas Peucker)于1973年提出,并在之后的数十年中由其他学者予以完善。
算法步骤如下图所示:
1、其中曲线0是原始数据,设置阈值距离为5m,开始循环;
2、循环查找距离起始点最远的点c,其距离为b,判断若b<5则直接以起始点连线为结果,否则将曲线以c分成前后2段;
3、分别对前后两段执行迭代步骤2;
4、得到最终结果;
2、算法实现
由之前的距离公式(https://blog.csdn.net/chaoyang89111/article/details/89440855)很容易写出格拉斯-普克的实现代码。
//计算距离
function calculationDistance(point1, point2){
let lat1 = point1.C_COORDINATEY;
let lat2 = point2.C_COORDINATEY;
let lng1 = point1.C_COORDINATEX;
let lng2 = point2.C_COORDINATEX;
let radLat1 = lat1 * Math.PI / 180.0;
let radLat2 = lat2 * Math.PI / 180.0;
let a = radLat1 - radLat2;
let b = (lng1 * Math.PI / 180.0) - (lng2 * Math.PI / 180.0);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
return s * 6370996.81;
};
//计算垂距
function distToSegment(start, end, center){
//下面用海伦公式计算面积
let a = Math.abs(calculationDistance(start, end));
let b = Math.abs(calculationDistance(start, center));
let c = Math.abs(calculationDistance(end, center));
let p = (a + b + c) / 2.0;
let s = Math.sqrt(Math.abs(p * (p - a) * (p - b) * (p - c)));
return s * 2.0 / a;
};
//递归方式压缩轨迹
function compressLine (coordinate, result, start, end, dMax){
if (start < end) {
let maxDist = 0;
let currentIndex = 0;
let startPoint = coordinate[start];
let endPoint = coordinate[end];
for (let i = start + 1; i < end; i++) {
let currentDist = distToSegment(startPoint, endPoint, coordinate[i]);
if (currentDist > maxDist) {
maxDist = currentDist;
currentIndex = i;
}
}
if (maxDist >= dMax) {
//将当前点加入到过滤数组中
result.push(coordinate[currentIndex]);
//将原来的线段以当前点为中心拆成两段,分别进行递归处理
compressLine(coordinate, result, start, currentIndex, dMax);
compressLine(coordinate, result, currentIndex, end, dMax);
}
}
return result;
};
/**
*
*@param coordinate 原始轨迹Array<{latitude,longitude}>
*@param dMax 允许最大距离误差
*@return douglasResult 抽稀后的轨迹
*
*/
function douglasPeucker(coordinate, dMax ){
if (!coordinate || !(coordinate.length > 2)) {
return null;
}
coordinate.forEach((item, index) => {
item.key = index;
});
let result = compressLine(coordinate, [], 0, coordinate.length - 1, dMax);
result.push(coordinate[0]);
result.push(coordinate[coordinate.length - 1]);
let resultLatLng = result.sort((a, b) => {
if (a.key < b.key) {
return -1;
} else if (a.key > b.key)
return 1;
return 0;
});
resultLatLng.forEach((item) => {
item.key = undefined;
});
return resultLatLng;
};
3、算法结果
如下图红色为抽稀后结果,蓝线为原始数据,原始总共2589,处理后还剩313个点,压缩率非常不错 。