道格拉斯-普克 Douglas-Peuker(DP算法) python php实现

news2024/12/24 20:18:04

废话不多说,直接开干!

最近在做一个车联网项目,有一个场景是车辆定时上报当前所在经纬度等位置信息上报给平台,平台通过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 plt


class Point:
    """点类"""
    x = 0.0
    y = 0.0
    index = 0  # 点在线上的索引

    def __init__(self, x, y, index):
        self.x = x
        self.y = y
        self.index = index


class 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.coords
        for 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 = None

        if (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 = True
        else:
            swichvalue = False

        if (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");//稀释万分之一

运行结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2080614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode面试经典150题-17.电话号码的字母组合

突然发现回溯题最近考的好多&#xff0c;其实没啥技术含量 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public static final char[][] digitsCharArr {{},{},{a,b,c},/**数字2可以代表的字符 */{d,e,f},{g,h,i},{j,k,l},{m,n,o},{p,q,r,s},{t,u,v},{w,x…

【数据结构入门】二叉树之堆排序及链式二叉树

目录 前言 一、堆排序 1.概念 2.堆排序思想 3.具体步骤 4.实现 5.复杂度 二、堆的应用——TopK问题 三、链式二叉树 1.二叉树创建 2.二叉树遍历 1&#xff09;前序、中序以及后序遍历 2&#xff09;层序遍历 3.结点个数以及高度 1&#xff09;结点个数&#xff1a…

阿里巴巴中间件canal的搭建和使用以及linux命令下使用mail发送html格式的邮件

一、阿里巴巴中间件canal的搭建和使用 canal可以用来监控数据库数据的变化(binlog日志)&#xff0c;从而获得指定数据的变化。canal是应阿里巴巴存在杭州和美国的双机房部署&#xff0c;存在跨机房同步的业务需求时开始逐步的尝试基于数据库的日志解析&#xff0c;获取增量变更…

Netty三

Netty TCP拆包粘包 二次编码方式 常用二次解码器 网络应用程序基本步骤 数据结构设计 完善客户端 客户端实例 Netty编程易错点

C++ 设计模式——适配者模式

C 设计模式——适配者模式 C 设计模式——适配者模式1. 主要组成成分2. 逐步构建适配者模式2.1 目标抽象类定义2.2 源类实现2.3 适配器类实现2.4 客户端 3. 适配者模式 UML 图适配者模式 UML 图解析 5. 类适配者6. 适配者模式的优点7. 适配者模式的缺点8. 适配者模式适用场景总…

永久去除windows11推荐产品的软件

永久去除windows11推荐产品的软件 去除windows11 推荐的项目&#xff0c;并用来固定软件 要求 22621及以上版本 企业版&#xff0c;专业教育版&#xff0c;教育版&#xff08;可以自行找工具切换&#xff0c;无需重装系统,非常方便的。&#xff09; [软件原创作者]&#xff…

【Python】Python 函数综合指南——从基础到高阶

文章目录 Python 函数综合指南1. 函数介绍1.1 什么是函数&#xff1f;1.2 定义函数示例&#xff1a;1.3 调用函数1.4 函数参数1.4.1 必需参数1.4.2 默认参数1.4.3 关键字参数1.4.4 可变长度参数 2. Python 内置函数2.1 字符串处理函数示例&#xff1a; 2.2 数学函数示例&#x…

音视频相关

ffmpeg 安装 1. 源码安装 git clone https://git.ffmpeg.org/ffmpeg.git 2. 配置 编译 安装 ./configure --prefix/usr/local/ffmpeg --enable-debug3 --enable-shared --disable-static --disable-x86asm --enable-ffplaymake -jnproc && make install Q: 没有ff…

C++ 基础学习

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream>using namespace std;int main() {cout<<"请输入字符串:";string str;getline(cin,str);int num0;int alp0;int spa0;int other0;int …

大语言模型-GPT3-Language Models are Few-Shot Learners

一、背景信息&#xff1a; GPT3是于2020 年由OpenAI 发布的预训练语言模型。 GPT3在自然语言处理&#xff08;NLP&#xff09;任务中表现出色&#xff0c;可以生成连贯的文本、回答问题、进行对话等。 GPT3的网络架构继续沿用GPT1、GPT2的是多层Transformer Decoder改的结构。…

论文笔记:GEO-BLEU: Similarity Measure for Geospatial Sequences

22 sigspatial 1 intro 提出了一种空间轨迹相似性度量的方法比较了两种传统相似度度量的不足 DTW 基本特征是它完全对齐序列以进行测量&#xff0c;而不考虑它们之间共享的局部特征这适用于完全对齐的序列&#xff0c;但不适用于逐步对齐没有太多意义的序列BLEU 适用于不完全…

MVSEP-MDX23容器构建详细教程

一、介绍 模型GitHub网址&#xff1a;MVSEP-MDX23-music-separation-model/README.md 在 main ZFTurbo/MVSEP-MDX23-音乐分离模型 GitHub 上 在音视频领域&#xff0c;把已经发布的混音歌曲或者音频文件逆向分离一直是世界性的课题。音波混合的物理特性导致在没有原始工程文件…

股指期货的交易规则有哪些?

股指期货作为一种金融衍生品&#xff0c;其合约条款和交易规则是投资者必须了解的重要内容。以下是关于股指期货合约条款及交易规则的详细解释&#xff1a; 一、合约乘数 沪深300指数期货合约的乘数为每点人民币300元。 中证500股指期货合约的乘数为每点200元。 上证50股指…

【iOS】Masonry学习

Masonry学习 前言NSLayoutConstraintMasonry学习mas_equalTo和equalToMasonry的优先级Masorny的其他写法 Masonry的使用练习 前言 Masonry是一个轻量级的布局框架。通过链式调用的方式来描述布局&#xff0c;是排版代码更加简洁易读。masonry支持iOS和Mac OS X。相比原生的NSL…

浅谈【数据结构】图-最短路径问题

目录 1、最短路径问题 2、迪杰斯特拉算法 3、算法的步骤 谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注 没错&#xff0c;说的就是你&#xff0c;不用再怀疑&#xff01;&#xff01;&#xff01; 希望我的文章内容能对你有帮助&#xff0c;一起努力吧&#xff0…

足球数据分析管理系统(JSP+java+springmvc+mysql+MyBatis)

项目文件图 项目介绍 随着足球运动的专业化和商业化程度不断提高&#xff0c;对运动员的表现进行分析和管理变得越来越重要。一个高效的足球运动员数据管理系统可以帮助教练团队、球探和俱乐部管理层全面了解每位运动员的训练情况、比赛表现、身体状态和其他关键指标。这样的系…

Leetcode JAVA刷刷站(99)恢复二叉搜索树

一、题目概述 二、思路方向 要解决这个问题&#xff0c;我们可以采用中序遍历二叉搜索树&#xff08;BST&#xff09;的方法&#xff0c;因为中序遍历BST会返回一个有序的数组。由于只有两个节点被错误地交换了&#xff0c;所以中序遍历的结果中将有两个位置上的元素是逆序的。…

AD7606芯片驱动-FPGA实现

简介 AD7606是一款16位ADC芯片&#xff0c;可实现8通道并行采集&#xff0c;每通道最大速度可达1M&#xff0c;可实现多种模式数据采集。 介绍 本次FPGA使用的是8通道串行采样模式&#xff0c;设计中所用到的AD7606引脚说明如下&#xff1a; 名称定义CONVST同步采集转换开始信…

并发服务器开发基础

一、服务器模型 1. 单循环服务器&#xff1a; 单循环服务器在同一时刻只能处理一个客户端的请求。由于其结构简单&#xff0c;适合低负载的场景&#xff0c;但在并发请求增加时可能导致性能问题。 2. 并发服务器模型&#xff1a; 并发服务器可以同时响应多个客户端…

openzgy编译和测试应用

zgy是仅次于segy重要的地震数据格式,最早在petrel软件中使用,目前已基本成为行业标准,具有更快的数据存储效率。openzgy是其开源版本。 ZGY文件格式由Schlumberger公司开发,用于存储地震解释的三维数据。OpenZGY库提供了读写该格式的能力。存在C++和Python两种版本。对于P…