一种改进的道格拉斯-普克算法以及C++实现

时间:2021-08-06 23:39:09
 

道格拉斯—普克(Douglas一Peukcer)算法

分类: 地图||地理数据   1431人阅读  评论(0)  收藏  举报

Douglas一Peukcer算法由D.Douglas和T.Peueker于1973年提出,简称D一P算法,是目前公认的线状要素化简经典算法。现有的线化简算法中,有相当一部分都是在该算法基础上进行改进产生的。它的优点是具有平移和旋转不变性,给定曲线与阂值后,抽样结果一定。本章线化简重点讲解该算法。

算法的基本思路是:对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax ,用dmax与限差D相比:若dmax < ,这条曲线上的中间点全部舍去;若dmax ≥,保留dmax 对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法。

算法的详细步骤如下:

(1) 在曲线首尾两点间虚连一条直线,求出其余各点到该直线的距离,如图3(1)。

(2) 选其最大者与阈值相比较,若大于阈值,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去,如图3(2),第4点保留。

(3) 依据所保留的点,将已知曲线分成两部分处理,重复第1、2步操作,迭代操作,即仍选距离最大者与阈值比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差的曲线点坐标,如图3(3)、(4)依次保留第6点、第7点,舍去其他点,即完成线的化简。

一种改进的道格拉斯-普克算法以及C++实现




 

GIS矢量数据化简:一种改进的道格拉斯-普克算法以及C++实现

分类: GIS底层开发   124人阅读  评论(0)  收藏  举报

        既然今天有时间,就多写几篇博文算了,也为了明天出去玩好好放松一下。

       GIS领域的同志都知道,传统的道格拉斯-普克算法都是递归实现。然而有时候递归的层次太深的话会出现栈溢出的情况。在此,介绍一种非递归的算法。

       要将递归算法改为非递归算法,一般情况下分为两种场景。第一种是问题定义是递归的,如阶乘、斐波那契数列等,对于这类问题,改为递归算法很简单,直接用迭代来做。另外一种是过程是递归的,如本文的道格拉斯-普克算法,对于这类问题呢,一般是用栈(stack)来记录中间结果,最后得到结果。

        为了保证极值点的不被舍去,将曲线在弯曲极值点分为两段处理,弯曲极值点通过中间点与相邻两个顶点的角度度量。然而传统的Douglas-Peucker算法一般在计算过程中没有考虑到记录中间最大的距离的节点,造成循环时间长、递归嵌套层次太深,从而影响了程序的运行效率。本文提出一种结合栈数据结构的分段Douglas-Peucker算法,它从曲线的一端出发,首先将第一个点和最后一个点作为改进的Douglas-Peucker算法的工作区间,然后判断最远点的距离是否大于阈值,这样完成线要素的综合化简。改进D-P算法的具体步骤如下:

(1)寻找曲线曲率最大的点,将曲线以此点为界一分为二分成两部分,对于每一部分都有点列 。然后分别处理这两段曲线。

(2)对于第一段曲线,有矢量的离散点序列 ,设 并且 ,连接 组成一条线段。生成一个栈 ,将 点入栈 。

(3)在 之间的点中寻找与 线段距离最大的点,记为 。

(4)判断 点到 的距离是否小于阈值,若否,则设 ,并将 加入到特征点序列,将 压入栈 ,用线段连接 ,回到(3)。若是,执行第(5)步。

(5)判断 是否等于 的栈顶元素,若否,则设 、 , 表示栈顶,用线段连接 ,回到(3)。若是,执行(6)。

(6)判断 是否等于 ,若否,则设 、 ( 表示 的栈顶的下一点),用线段连接 ,栈顶元素出栈,回到(3)。

(7)当栈为空时,第一段曲线计算结束。处理第二段曲线,重复(1)~(7)。

改进算法的程序流程图如下图所示。

                                                                                                         一种改进的道格拉斯-普克算法以及C++实现

 改进Douglas-Peucker流程图

 

本文提出的改进算法虽然在编程上面比较复杂,但是能够减少中间重复循环的次数。有了上面的流程图之后,那么代码就相对简单了。

[cpp]  view plain copy
  1. void DouglasPeucker(LineVertex *V,int &i,int &j,double e)  
  2. {  
  3.     double dist = 9999;  
  4.     int f = 0;          //最大距离的点的序号  
  5.     stack<int> tempVertex;            //STL实现的栈  
  6.     tempVertex.push(j);  
  7.   
  8.     do   
  9.     {  
  10.         //循环i和j之间距离直线ij最大的点  
  11.         FindSplit(*V,i,j,&f,&dist);  
  12.   
  13.         if (dist > e)      //大于阈值  
  14.         {  
  15.             (*V)[f].flag = true;  
  16.   
  17.             j = f;  //更新B值  
  18.   
  19.             tempVertex.push(f);                 //记录最大距离点,放入栈中存储  
  20.             continue;  
  21.         }  
  22.   
  23.         else              
  24.         {  
  25.             if (!tempVertex.empty() && j != tempVertex.top())   //判断后一点是否和当前栈顶相等  
  26.             {  
  27.                 i = f;  
  28.                 j = tempVertex.top();  
  29.                 continue;  
  30.             }  
  31.             else  
  32.             {  
  33.                 if (j != V->size())      //判断最后一个点是否和当前线段的B点重合  
  34.                 {  
  35.                     i = j;  
  36.                     if (!tempVertex.empty())  
  37.                     {  
  38.                         deque<int> cont = tempVertex._Get_container();  
  39.                         if (cont.size() > 1) //栈中至少还有两个点  
  40.                         {  
  41.                             j = cont[cont.size()-2];  
  42.                         }  
  43.                         else if (cont.size() == 1)  //栈中只有一个点  
  44.                         {  
  45.                             j = cont[cont.size()-1];  
  46.                         }  
  47.                         tempVertex.pop();  
  48.                         continue;  
  49.                     }  
  50.                 }  
  51.             }  
  52.         }  
  53.     } while (!tempVertex.empty());  
  54.   
  55. }  

 

代码不是完整的,其中有调用到了另外一个函数,就是找曲线中离曲线两端点的直线的距离的最大值点,这个过程很简单,就不讲了,还有自己定义的一些数据结构,本文只是讲解这一种算法的思想,其中算法描述部分由公式,原文是在word中编辑的,如果看不懂就直接看流程图还清晰点。