道格拉斯-普克 抽稀算法 附javascript实现

简介: 道格拉斯-普克抽稀算法,是用来对大量冗余的图形数据点进行压缩以提取必要的数据点。

道格拉斯-普克抽稀算法,是用来对大量冗余的图形数据点进行压缩以提取必要的数据点。该算法实现抽稀的过程是:先将一条曲线首尾点虚连一条直线,求其余各点到该直线的距离,取其最大者与规定的临界值相比较,若小于临界值,则将直线两端间各点全部舍去,否则将离该直线距离最大的点保留,并将原线条分成两部分,对每部分线条再实施该抽稀过程,直到结束。抽稀结果点数随选取限差临界值的增大而减少,应用时应根据精度来选取限差临界值,以获得最好的效果。


以下转载自:垂距法与道格拉斯-普克法删除冗余顶点效率的比较

道格拉斯- 普克法可描述为:将一条曲线首末顶点虚连一条直线 ,求出其余各顶点到该直线的距离 ,选其最大者与规定的限差相比较 ,若小于等于限差 ,则将直线两端间各点全部删去;若大于限差 ,则离该直线距离最大的顶点保留 ,并以此为界 ,把曲线分为两部分 ,对这两部分重复使用上述方法 ,直至最终无法作进一步的压缩为止 (见图 3)。
img

道格拉斯 2 普克法有一个十分突出的优点 ,即它是一个整体算法 ,在一般情况下可保留较大弯曲形态上的特征点。经道格拉斯-普克法压缩后得到的图形如图 4所示。由于该算法可准确删除小弯曲上的定点 ,故能从体上有效地保持线要素的形态特征。正是因为道格拉斯-普克法具有这样突出的优点 ,所以已经在线要素地自动制图中得到了较广泛的应用。但道格拉斯- 普克法较垂距法复杂 ,且通常编程实现时需要采用递归方 ,有一定的难度。
img

转载end

以下是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">
    var K = function (a, b, c, d) {
        var a1 = document.getElementById(a);
        if (a1 != null)
            return a1;
        else {
            a = document.createElement(a);
            for (var i in c) {
                a.style[i] = c[i];
            }

            return a;
        }
    };
    K.isArr = function (object) {
        return object && typeof object === 'object' &&
            typeof object.length === 'number' &&
            typeof object.splice === 'function' &&
            //判断length属性是否是可枚举的 对于数组 将得到false
            !(object.propertyIsEnumerable('length'));
    }
    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

目录
相关文章
|
4月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
4月前
|
JavaScript 算法 前端开发
JS算法必备之Array常用操作方法
这篇文章详细介绍了JavaScript中数组的创建、检测、转换、排序、操作方法以及迭代方法等,提供了数组操作的全面指南。
JS算法必备之Array常用操作方法
|
4月前
|
JavaScript 算法 前端开发
"揭秘Vue.js的高效渲染秘诀:深度解析Diff算法如何让前端开发快人一步"
【8月更文挑战第20天】Vue.js是一款备受欢迎的前端框架,以其声明式的响应式数据绑定和组件化开发著称。在Vue中,Diff算法是核心之一,它高效计算虚拟DOM更新时所需的最小实际DOM变更,确保界面快速准确更新。算法通过比较新旧虚拟DOM树的同层级节点,递归检查子节点,并利用`key`属性优化列表更新。虽然存在局限性,如难以处理跨层级节点移动,但Diff算法仍是Vue高效更新机制的关键,帮助开发者构建高性能Web应用。
76 1
|
5月前
|
数据采集 算法 JavaScript
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
JavaScript字符串搜索涵盖`indexOf`、`includes`及KMP算法。`indexOf`返回子字符串位置,`includes`检查是否包含子字符串。KMP是高效的搜索算法,尤其适合长模式匹配。示例展示了如何在数据采集(如网页爬虫)中使用这些方法,结合代理IP进行安全搜索。代码示例中,搜索百度新闻结果并检测是否含有特定字符串。学习这些技术能提升编程效率和性能。
130 1
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
|
5月前
|
算法 JavaScript
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
70 0
JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)
|
5月前
|
算法 JavaScript
JS 【详解】二叉树(含二叉树的前、中、后序遍历技巧和算法实现)
JS 【详解】二叉树(含二叉树的前、中、后序遍历技巧和算法实现)
53 0
|
5月前
|
算法 JavaScript
JS 【算法】二分查找
JS 【算法】二分查找
37 0
|
5月前
|
缓存 算法 前端开发
前端 JS 经典:LRU 缓存算法
前端 JS 经典:LRU 缓存算法
99 0
|
5月前
|
存储 JavaScript 搜索推荐
js【详解】arr.sort()数组排序(内含十大经典排序算法的js实现)
js【详解】arr.sort()数组排序(内含十大经典排序算法的js实现)
37 0
|
6月前
|
算法 JavaScript 安全
一篇文章讲明白JavaScript_提交表单和MD5算法密码加密
一篇文章讲明白JavaScript_提交表单和MD5算法密码加密
56 0