Douglas—Peucker算法
是将曲线近似表示为一系列点,并减少点的数量的一种算法。它的优点是具有平移和旋转不变性,给定曲线与阈值后,抽样结果一定。
算法思路:
对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax,用dmax与限差D相比:
若dmax<D,这条曲线上的中间点全部舍去;
若dmax≥D,保留dmax对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法。
传统的道格拉斯算法是采用递归的方法,算法的核心是求点到直线的距离。设置一个距离阈值,当点到线的距离小于该阈值,那么点视为可以删除的点。最后剩下的点即为最终结果
程序下载:地图投影与抽稀
1、算法输入:inPoint为待处理的点列数据, D为阈值
public class MyPoint
{
public double x;
public double y;
}
public void Do(List<MyPoint> inPoint, double D )
{
int n1 = 0;
int n2 = inPoint.Count;
Douglas(inPoint, n1, n2, D);
}
2、调用Douglas算法,n1, n2分别为首尾点的位置标记
public struct line//记录直线参数的结构
{
public double k;
public double b;
}
/// <summary>
/// 求直线斜率与截距
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
private line parameter(MyPoint p1, MyPoint p2)
{
double k, b;
line newcs = new line();
k = (p2.y - p1.y) / (p2.x - p1.x);
b = p1.y - k * p1.x;
newcs.k = k;
newcs.b = b;
return newcs;
}
/// <summary>
/// 求点到直线的距离
/// </summary>
/// <param name="dot"></param>
/// <param name="cs"></param>
/// <returns></returns>
private double distance(MyPoint dot, line cs)
{
double dis = (Math.Abs(cs.k * dot.x - dot.y + cs.b)) / Math.Sqrt(cs.k * cs.k + 1);
return dis;
}
List<MyPoint> outPoint = new List<MyPoint>();//保存结果
private void Douglas(List<MyPoint> InPoints, int n1, int n2, double d)
{
int num = InPoints.Count;
//List<MyPoint> OutPoint = new List<MyPoint>();
int Max = 0;//定义拥有最大距离值的点的编号
line MyLine = new line();
MyLine = parameter(InPoints[n1], InPoints[n2 - 1]); //得到收尾点连线组成的虚线
double MaxDistance = 0;
if (d == 0)
{
outPoint = InPoints;
}
else
{
for (int i = n1 + 1; i < n2 - 1; i++)
{
double pDistance = distance(InPoints[i], MyLine);
if (pDistance > d && pDistance >= MaxDistance)
{
Max = i;
MaxDistance = pDistance;
}
}
if (Max == 0) //如果最大值距离小于阈值直接返回首位
{
outPoint.Add(InPoints[n1]);
outPoint.Add(InPoints[n2 - 1]);
return;
}
else if (n1 + 1 == Max && n2 - 2 != Max) //第二个点为最大距离
{
Douglas(InPoints, Max + 1, n2, d);
outPoint.Add(InPoints[n1]);
}
else if (n2 - 2 == Max && n1 + 1 != Max) //倒数第二个点为最大距离
{
Douglas(InPoints, 0, Max + 1, d);
outPoint.Add(InPoints[n2 - 1]);
}
else if (n1 + 1 == Max && n2 - 2 == Max) //夹中间
{
outPoint.Add(InPoints[Max - 1]);
outPoint.Add(InPoints[Max]);
outPoint.Add(InPoints[Max + 1]);
return;
}
else
{
Douglas(InPoints, n1, Max + 1, d);
Douglas(InPoints, Max, n2, d);
}
}
}
调用算法对点坐标压缩