本文主要是介绍道格拉斯-普克 Douglas-Peuker(DP算法) python php实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
废话不多说,直接开干!
最近在做一个车联网项目,有一个场景是车辆定时上报当前所在经纬度等位置信息上报给平台,平台通过web页面在高德地图上展示车辆行驶路径。
说明
道格拉斯-普克算法 (Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法、迭代适应点算法、分裂与合并算法)是将曲线近似表示为一系列点,并减少点的数量的一种算法。它的优点是具有平移和旋转不变性,给定曲线与阈值后,抽样结果一定。—摘自百度百科
python 代码
安装模块Shapely
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple Shapely
# -*- coding:utf-8 -*-
"""
道格拉斯算法的实现
程序需要安装shapely模块
"""
import math
from shapely import wkt, geometry
import matplotlib.pyplot as pltclass Point:"""点类"""x = 0.0y = 0.0index = 0 # 点在线上的索引def __init__(self, x, y, index):self.x = xself.y = yself.index = indexclass Douglas:"""道格拉斯算法类"""points = []D = 1 # 容差def readPoint(self):"""生成点要素"""g = wkt.loads("LINESTRING(1 4,2 3,4 2,6 6,7 7,8 6,9 5,10 10)")coords = g.coordsfor i in range(len(coords)):self.points.append(Point(coords[i][0], coords[i][1], i))def compress(self, p1, p2):"""具体的抽稀算法"""swichvalue = False# 一般式直线方程系数 A*x+B*y+C=0,利用点斜式,分母可以省略约区# A=(p1.y-p2.y)/math.sqrt(math.pow(p1.y-p2.y,2)+math.pow(p1.x-p2.x,2))A = (p1.y - p2.y)# B=(p2.x-p1.x)/math.sqrt(math.pow(p1.y-p2.y,2)+math.pow(p1.x-p2.x,2))B = (p2.x - p1.x)# C=(p1.x*p2.y-p2.x*p1.y)/math.sqrt(math.pow(p1.y-p2.y,2)+math.pow(p1.x-p2.x,2))C = (p1.x * p2.y - p2.x * p1.y)m = self.points.index(p1)n = self.points.index(p2)distance = []middle = Noneif (n == m + 1):return# 计算中间点到直线的距离for i in range(m + 1, n):d = abs(A * self.points[i].x + B * self.points[i].y + C) / math.sqrt(math.pow(A, 2) + math.pow(B, 2))distance.append(d)dmax = max(distance)if dmax > self.D:swichvalue = Trueelse:swichvalue = Falseif (not swichvalue):for i in range(m + 1, n):del self.points[i]else:for i in range(m + 1, n):if (abs(A * self.points[i].x + B * self.points[i].y + C) / math.sqrt(math.pow(A, 2) + math.pow(B, 2)) == dmax):middle = self.points[i]self.compress(p1, middle)self.compress(middle, p2)def printPoint(self):"""打印数据点"""for p in self.points:print( "%d,%f,%f" % (p.index, p.x, p.y))def main():"""测试"""d = Douglas()d.readPoint()# d.printPoint()# 结果图形的绘制,抽稀之前绘制fig = plt.figure()a1 = fig.add_subplot(121)dx = []dy = []for i in range(len(d.points)):dx.append(d.points[i].x)dy.append(d.points[i].y)a1.plot(dx, dy, color='g', linestyle='-', marker='+')d.compress(d.points[0], d.points[len(d.points) - 1]) #稀释后轨迹# 抽稀之后绘制dx1 = []dy1 = []a2 = fig.add_subplot(122)for p in d.points:print(p.x,p.y)dx1.append(p.x)dy1.append(p.y)a2.plot(dx1, dy1, color='r', linestyle='-', marker='+')plt.show()if __name__ == '__main__':main()
运行结果
php代码
/*** 根据两个点求直线方程 ax+by+c=0* @param $xy1 string 点1,例如"1,1"* @param $xy2 string 点2,例如"2,2"* @return array*/function getLineByPoint($xy1, $xy2){$x1 = explode(",", $xy1)[0];//第一个点的横坐标$y1 = explode(",", $xy1)[1];//第一个点的纵坐标$x2 = explode(",", $xy2)[0];//第二个点的横坐标$y2 = explode(",", $xy2)[1];//第二个点的横坐标$a = $y2 - $y1;$b = $x1 - $x2;$c = ($y1 - $y2) * $x1 - $y1 * ($x1 - $x2);return [$a, $b, $c];}/*** 稀疏点* @param $points array 参数为["1,2","2,3"]点集* @param $max float 阈值,越大稀疏效果越好但是细节越差* @return array*/function sparePoints($points, $max){if (count($points) < 3) {return $points;}// var_dump($points);die;$xy1 = $points[0];//取第一个点$xy1 = $xy1['glng'].','.$xy1['glat'];$xy2 = end($points);//取最后一个点$xy2 = $xy2['glng'].','.$xy2['glat'];
// var_dump(end($points)['glat']);die;list($a, $b, $c) = getLineByPoint($xy1, $xy2);//获取直线方程的a,b,c值$ret = [];//最后稀疏以后的点集$dmax = 0;//记录点到直线的最大距离$split = 0;//分割位置for ($i = 1; $i < count($points) - 1; $i++) {$d = getDistanceFromPointToLine($a, $b, $c, $points[$i]);if ($d > $dmax) {$split = $i;$dmax = $d;}}if ($dmax>$max) {//如果存在点到首位点连成直线的距离大于max的,即需要再次划分$child1 = sparePoints(array_slice($points, 0, $split + 1), $max);//按split分成左边一份,递归$child2 = sparePoints(array_slice($points, $split), $max);//按split分成右边一份,递归//因为child1的最后一个点和child2的第一个点,肯定是同一个(即为分割点),合并的时候,需要注意一下$ret = array_merge($ret, $child1, array_slice($child2, 1));return $ret;} else {//如果不存在点到直线的距离大于阈值的,那么就直接是首尾点了return [$points[0], end($points)];}}$gps_list= ["118.7727996753945,34.1246971539229","118.77485177448638,34.124702346094594","118.7831576932094,34.13298337245958","118.78533669945193,34.133335134266446", ];$data = sparePoints($gps_list, "0.0001");//稀释万分之一
运行结果
这篇关于道格拉斯-普克 Douglas-Peuker(DP算法) python php实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!