道格拉斯-普克算法(Ramer–Douglas–Peucker)曲线降采样


背景

在最近开发的某系统中,有一需求是在map上显示路径,并在路径上分段打箭头、点和文字信息(时间)。

测试环境(mock数据)理想效果


看起来还不错是吧
然鹅,但由于真实环境点位数据过于密集,导致显示效果十分魔幻,看起来是这样婶的。(害)

然后呢,突然想起来之前大学时侯做acm的一个路径拟合题和这个需求很类似,就在此分享一下吧
(虽然好像这个算法是由后端实现)

Ramer–Douglas–Peucker算法

1. 简介

Ramer–Douglas–Peucker算法,又称道格拉斯-普克算法算法或迭代端点拟合算法,是一种将由多点组成的曲线(折线)降采样为点数较小的类似曲线(折线)的算法。

2. 算法思想

起始曲线是一组有序的点或线,距离维度 ε(可以理解为拟合度) > 0。
该算法采用递归思想。最初,运算点集合为所有点。首先,拟合出第一个点和最后一个点的直线,计算当前运算集中所有点到这条直线的距离(循环),如果这个距离(d)<ε,那么可以将除了首尾的两个点以外的所有点去除,形成一个简单线段。否则,如果距离(d)>ε,这个点将被保留,并由此点将原有运算集划分为两个子集,并递归运算这个流程。
当递归完成后,可以生成一条新的输出曲线,该曲线由所有且仅由那些被标记为保留的点组成。
说了这些,有轻微阅读障碍的XDM估计已经躺下了(比如我),所以这里放上一张图来帮助理解。

3. 参数控制

ε(阈值),越小,直线越平滑,越大,直线越锐利。

4. 伪代码

function 道格拉斯(运算点集, 阈值) {
  // 找出两点间点的最大距离
  最大距离 = 0;
  距离最大点位置 = 0;
  length = 运算集.length;
  for (let i = 0; i < length - 1; i++) {
    d = 计算点到直线距离(
      运算点集[i],
      拟合直线方程(运算点集[0], 运算点集[length - 1])
    );
    if (d > 最大距离) {
      距离最大点位置 = i;
      最大距离 = d;
    }
  }

  结果输出 = [];

  // 如果最大距离小于阈值,就拟合
  if (最大距离 > 阈值) {
    // 继续计算
    左子集 = 道格拉斯(运算点集合.slice(0, 距离最大点位置), 阈值);
    右子集 = 道格拉斯(运算点集.slice(距离最大点位置, length), 阈值);
    结果输出 = 左子集.concat(右子集);
  } else {
    // 拟合
    结果输出 = [运算点集[0], 运算点集[length - 1]];
  }
  return 结果输出;
}

5.后记

  • 算法复杂度O(n log n)

End


文章作者: Bryan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryan !
  目录