道格拉斯-普克抽稀算法,是用來對大量冗餘的圖形數據點進行壓縮以提取必要的數據點。該算法實現抽稀的過程是:先將一條曲線首尾點虛連一條直線,求其他各點到該直線的距離,取其最大者與規定的臨界值相比較,若小於臨界值,則將直線兩端間各點所有捨去,不然將離該直線距離最大的點保留,並將原線條分紅兩部分,對每部分線條再實施該抽稀過程,直到結束。抽稀結果點數隨選取限差臨界值的增大而減小,應用時應根據精度來選取限差臨界值,以得到最好的效果。javascript
如下轉載自:垂距法與道格拉斯-普克法刪除冗餘頂點效率的比較html
道格拉斯- 普克法可描述爲:將一條曲線首末頂點虛連一條直線 ,求出其他各頂點到該直線的距離 ,選其最大者與規定的限差相比較 ,若小於等於限差 ,則將直線兩端間各點所有刪去;若大於限差 ,則離該直線距離最大的頂點保留 ,並以此爲界 ,把曲線分爲兩部分 ,對這兩部分重複使用上述方法 ,直至最終沒法做進一步的壓縮爲止 (見圖 3)。
java
道格拉斯 2 普克法有一個十分突出的優勢 ,即它是一個總體算法 ,在通常狀況下可保留較大彎曲形態上的特徵點。經道格拉斯-普克法壓縮後獲得的圖形如圖 4所示。因爲該算法可準確刪除小彎曲上的定點 ,故能從體上有效地保持線要素的形態特徵。正是由於道格拉斯-普克法具備這樣突出的優勢 ,因此已經在線要素地自動製圖中獲得了較普遍的應用。但道格拉斯- 普克法較垂距法複雜 ,且一般編程實現時須要採用遞歸方 ,有必定的難度。
git
轉載endgithub
如下是javascript版本的實現算法
<!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> <div id="processBefore" style="background-color:#ccc;height:100px;position:relative;"></div> <div id="processAfter" style="background-color:#ccc;height:100px;margin-top:10px;position:relative;"></div> </body> <script type="text/javascript" src="Items/js/K1.source.js"> var points = [{ x: 10, y: 10 }, { x: 20, y: 30 }, { x: 30, y: 12 }, { x: 35, y: 5 }, { x: 40, y: 22 }, { x: 50, y: 12 }, { x: 80, y: 40 }]; var Helper = { renderPointsTo: function(points, anchor) { var d; for (var i = 0, p, a = K(anchor); i < points.length; i++) { p = points[i]; if (a) { a.appendChild(K('div', {}, { position: 'absolute', left: p.x + 'px', top: p.y + 'px', width: '5px', height: '5px', backgroundColor: 'green', fontSize: '1px' })); } } } }; Helper.renderPointsTo(points, 'processBefore'); var DouglasPeucker = { getProcessPoints: function(points, tolerance) { /// <summary>獲取處理後的點</summary> /// <param name="points" type="Array">包含點的數組</param> /// <param name="tolerance" type="Float">取樣臨界值</param> /// <returns type="Array" /> if (!K.isArr(points) || !points.length) { //當points不是數組或沒有值時,直接返回一個空數組 return []; } if (points.length < 3) return points; //小於3個點時不抽稀,由於1個或2個點沒法進行抽稀 var firstPoint = 0, lastPoint = points.length - 1; //取開始點與結束點的下標 var pointIndexsToKeep = []; //保存須要點下標的數組 pointIndexsToKeep.push(firstPoint); pointIndexsToKeep.push(lastPoint); //開始與結束不進行處理,直接保留 while (points[firstPoint] == points[lastPoint]) { //處理閉合狀況,閉合時,強制斷開 lastPoint--; } this.reduce(points, firstPoint, lastPoint, tolerance, pointIndexsToKeep); //抽稀 var resultPoints = []; //返回的點數組 pointIndexsToKeep.sort(function(a, b) { //排序,這個可排可不排 return a - b; }); for (var i = 0; i < pointIndexsToKeep.length; i++) { resultPoints.push(points[pointIndexsToKeep[i]]); } return resultPoints; }, reduce: function(points, firstPoint, lastPoint, tolerance, pointIndexsToKeep) { /// <summary>抽稀處理</summary> /// <param name="points" type="Array">待抽稀的數組</param> /// <param name="firstPoint" type="Integer">起點</param> /// <param name="lastPoint" type="Integer">終點</param> /// <param name="tolerance" type="Float">取樣臨界值</param> /// <param name="pointIndexsToKeep" type="Array">保留點下標的數組</param> var maxDis = 0, idxFarthest = 0; //定義最大長度及最遠點的下標 for (var i = firstPoint, dis; i < lastPoint; i++) { dis = this.perpendicularDistance(points[firstPoint], points[lastPoint], points[i]); //獲取當前點到起點與 if (dis > maxDis) { //保存最遠距離 maxDis = dis; idxFarthest = i; } } if (maxDis > tolerance && idxFarthest != 0) { //若是最遠距離大於臨界值 pointIndexsToKeep.push(idxFarthest); this.reduce(points, firstPoint, idxFarthest, tolerance, pointIndexsToKeep); this.reduce(points, idxFarthest, lastPoint, tolerance, pointIndexsToKeep); } }, perpendicularDistance: function(beginPoint, endPoint, comparePoint) { /// <summary>計算給出的comparePoint到beginPoint與endPoint組成的直線的垂直距離</summary> /// <param name="beginPoint" type="Object">起始點</param> /// <param name="endPoint" type="Object">結束點</param> /// <param name="comparePoint" type="Object">比較點</param> /// <returns type="Float" /> //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle //Base = v((x1-x2)2+(y1-y2)2) *Base of Triangle* //Area = .5*Base*H *Solve for height //Height = Area/.5/Base var area = Math.abs(0.5 * (beginPoint.x * endPoint.y + endPoint.x * comparePoint.y + comparePoint.x * beginPoint.y - endPoint.x * beginPoint.y - comparePoint.x * endPoint.y - beginPoint.x * comparePoint.y)); var bottom = Math.sqrt(Math.pow(beginPoint.x - endPoint.x, 2) + Math.pow(beginPoint.y - endPoint.y, 2)); var height = area / bottom * 2; return height; } }; Helper.renderPointsTo(DouglasPeucker.getProcessPoints(points, 14), 'processAfter'); </script> </html>
宣傳下個人區塊管理框架Magix:https://github.com/thx/magix編程
歡迎試用Magix、star與fork數組