道格拉斯-普克算法(JavaScript实现)

需求:

有时候当移动速度很慢,GPS定位的轨迹点就非常的多,这时候为了缩减数据量,需要将不突出的点去掉。

思路:

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

(2)选其最大者与阈值相比较,若大于阈值,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去。
(3)依据所保留的点,将已知曲线分成两部分处理,重复第1、2步操作,迭代操作,即仍选距离最大者与阈值比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差的曲线点坐标

这里使用道格拉斯-普克算法实现,易于理解。效果对比图如下:

源代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <title>DouglasPeucker</title>    
</head>
<body>
   <canvas id="drawing" style="height:300px;100%"></canvas>
    <canvas id="drawing2" style="height:300px;100%"></canvas>
</body>
<script type="text/javascript" >

    var points1=[];
    var pointsshao=[];
    var pts=[]; 
    var hval=30;//阈
    
    //随机生成1000个点
    for(var i=0;i<1000;i++){
    var  oldx=i*10,oldy=Math.random()*150;
      points1.push([oldx,oldy,i]);
    }
    
    //求斜率
    function xielv(pt1,pt2)
    {
      var k,b;
      var canshu={};
      canshu.k=(pt1[1]-pt2[1])/(pt1[0]-pt2[0]);
      canshu.b=pt1[1]-canshu.k*pt1[0];
      return canshu;
    }
    //求点到直线的距离
    function distanceToline(pt,cs){
       return (Math.abs(cs.k*pt[0]-pt[1]+cs.b))/Math.sqrt(cs.k*cs.k+1);
    }
    
    //开始计算(道格拉斯普克算法)
    pts.push(points1[0]);
    countPoint(points1);
    pts.push(points1[points1.length-1]);

    //排序
    function sort(pts){
      for(var i=0;i<pts.length-1;i++)
      {
         var a=pts[i];
         var b=pts[i+1];
         if(b[2]>a[2]){
         pts[i]=b;
         pts[i+1]=a;
         for(var j=i;j>0;j--)
         {
             var c=pts[j-1];
             if(b[2]>c[2]){
               pts[j-1]=b;
               pts[j]=c;
             }
         }
         }
      }
    }
  //对坐标点进行取舍
 function countPoint(points){
 
       var maxD=0;
       var maxPoint=null;
       var maxindex=0;
       //大于2个点才开始计算
       if(points.length>2){
             var pt1=points[0];
           var pt2=points[points.length-1];
           var cs=xielv(pt1,pt2);
           for(var i=0;i<points.length;i++){
             var pt=points[i];
             var dis=distanceToline(pt,cs);
            //判断该线段中是否有点到由该线段端点组成的直线的距离大于限值
             if(dis>maxD)
             {
               maxD=dis;
               maxPoint=pt;
               maxindex=i;
             }
           }
               if(maxD>hval) //如果最大值就从该点位置将线段进行切分
             {
                 var pts1=points.slice(maxindex);//中分末尾数组
                 var pts2=points.slice(0,maxindex+1);//中分前面数组
                if(pts1.length>2 && pts2.length>2)
                {
                 if(!countPoint(pts1) && !countPoint(pts2)){  //如果两个线段都没有超过限制就结束计算
                   pts.push(maxPoint);
                 }
                }else if(pts1.length>2 && pts2.length<=2){ //计算pts1
                    if(!countPoint(pts1))pts.push(maxPoint);
                    
                }else if(pts1.length<=2 && pts2.length>2){ //计算pts2
                   if(! countPoint(pts2))pts.push(maxPoint);
                    
                }
             } return false;
       }
     }
    //由大到小
    sort(pts);
    drawWay("drawing2",pts);
    drawWay("drawing",points1)
//绘制曲线
function drawWay(name,points){ var drawing=document.getElementById(name); if(drawing.getContext){ var context=drawing.getContext("2d"); context.beginPath(); var oldx=points[0][0]; var oldy=points[0][1]; for(var i=0;i<points.length;i++){ var p=points[i]; context.moveTo(oldx,oldy); oldx=p[0]; oldy=p[1]; context.lineTo(oldx,oldy); } context.closePath(); context.stroke(); } } </script>
原文地址:https://www.cnblogs.com/GIScore/p/9840454.html