优化静止不动的GPS点(JS版)

1.理论依据:

连续的GPS点中,静止不动的一段或者多段这样的点序列。把这些点序列处理成一个点,也就是拿这些序列的第一个点即可。理论依据如下:从第二个点开始,每个点都和第一个点进行距离计算和比较。至少比较N个点。当百分之M的点距离在X内的话,就继续计算比对。直到后面Z个连续点超出X,那么第一个点到超出X的第一个点,也就是Z的第一个点之前的点认为是静止状态。则这个序列被认为是静止的GPS序列,取停止点的中心点即可。后面从继续从Z的第一个点循环做这件事若点不足N个,则不做任何处理。

2.降噪原理

在连续的GPS序列中,假设前半段静止,后半段运动。则后序所有点和第一个点进行距离计算后和时间的关系图如上。静止的点拟合后的结果一定是平行于X轴的,后半段的拟合在匀速的情况下,是一个0<角度<90度的线(变速运动的话时拟合的是曲线)

3.计算中心点

4.计算中心点核心代码(降噪)

function calculateGeographicalCenter(points) {if (!points || points.length === 0) {return null;}let xSum = 0;let ySum = 0;let zSum = 0;points.forEach(point => {const latRad = degreesToRadians(point.lat);const lonRad = degreesToRadians(point.lon);xSum += Math.cos(latRad) * Math.cos(lonRad);ySum += Math.cos(latRad) * Math.sin(lonRad);zSum += Math.sin(latRad);});const numPoints = points.length;const xAvg = xSum / numPoints;const yAvg = ySum / numPoints;const zAvg = zSum / numPoints;const lonAvg = Math.atan2(yAvg, xAvg);const hyp = Math.sqrt(xAvg * xAvg + yAvg * yAvg);const latAvg = Math.atan2(zAvg, hyp);return {lat: radiansToDegrees(latAvg),lon: radiansToDegrees(lonAvg)};
}function degreesToRadians(degrees) {return degrees * (Math.PI / 180);
}function radiansToDegrees(radians) {return radians * (180 / Math.PI);
}

5.识别停留点序列核心代码

function calculateDistance(point1, point2) {// 使用Haversine公式计算两个GPS点之间的距离const R = 6371e3; // 地球半径,单位为米const lat1 = point1.lat * Math.PI / 180; // 第一个点的纬度转换为弧度const lat2 = point2.lat * Math.PI / 180; // 第二个点的纬度转换为弧度const deltaLat = (point2.lat - point1.lat) * Math.PI / 180; // 纬度差转换为弧度const deltaLon = (point2.lon - point1.lon) * Math.PI / 180; // 经度差转换为弧度const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +Math.cos(lat1) * Math.cos(lat2) *Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); // Haversine公式的中间变量const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // Haversine公式的另一个中间变量return R * c; // 计算最终距离,单位为米
}function findStaticPoints(gpsPoints, N, M, X, Z) {const finalPoints = []; // 存储最终的轨迹点let i = 0;while (i < gpsPoints.length) {let countWithinThreshold = 0; // 记录在距离阈值内的点的数量let j = i + 1;// 从第i个点开始,比较后续的N个点for (; j < gpsPoints.length && j < i + N; j++) {if (calculateDistance(gpsPoints[i], gpsPoints[j]) <= X) {countWithinThreshold++; // 统计在距离阈值X内的点的数量}}// 如果在N个点中有至少M%的点在距离阈值内if (countWithinThreshold / N >= M / 100) {let staticEndIndex = i + N; // 静止状态的结束索引初始值// 检查后续点,直到找到连续Z个点超出距离阈值Xwhile (staticEndIndex < gpsPoints.length) {let outOfThresholdCount = 0; // 记录超出阈值的点的数量for (let k = staticEndIndex; k < staticEndIndex + Z && k < gpsPoints.length; k++) {if (calculateDistance(gpsPoints[i], gpsPoints[k]) > X) {outOfThresholdCount++;}}// 如果连续Z个点中有Z个点超出距离阈值X,则认为静止状态结束if (outOfThresholdCount >= Z) {break;}staticEndIndex++;}finalPoints.push(gpsPoints[i]); // 将静止状态的第一个点加入结果数组i = staticEndIndex; // 跳到静止状态结束的点继续处理} else {finalPoints.push(gpsPoints[i]); // 非静止状态点,直接加入结果数组i++; // 不满足静止条件,继续检查下一个点}}return finalPoints; // 返回处理后的轨迹点数组
}

6.完整方法

// N: 10, // 最少比较的点数
// M: 70, // 距离阈值内的点的百分比
// X: 35, // 距离阈值,单位为米
// Z: 10, // 判断静止状态结束的连续点数const pointsResult: any = {stopPoints: [], //记录所有停留中心点finalPoints: [] // 存储最终的轨迹点
};function clear() {pointsResult.stopPoints = []pointsResult.finalPoints = []}function findStaticPoints(gpsPoints, N = 10, M = 70, X = 50, Z = 10) {clear()let i = 0;while (i < gpsPoints.length) {let countWithinThreshold = 0; // 记录在距离阈值内的点的数量let j = i + 1;// 从第i个点开始,比较后续的N个点for (; j < gpsPoints.length && j < i + N; j++) {if (calculateDistance(gpsPoints[i], gpsPoints[j]) <= X) {countWithinThreshold++; // 统计在距离阈值X内的点的数量}}// 如果在N个点中有至少M%的点在距离阈值内if (countWithinThreshold / N >= M / 100) {let staticEndIndex = i + N; // 静止状态的结束索引初始值const staticPointsSequence = gpsPoints.slice(i, i + N);// 检查后续点,直到找到连续Z个点超出距离阈值Xwhile (staticEndIndex < gpsPoints.length) {let outOfThresholdCount = 0; // 记录超出阈值的点的数量for (let k = staticEndIndex; k < staticEndIndex + Z && k < gpsPoints.length; k++) {if (calculateDistance(gpsPoints[i], gpsPoints[k]) > X) {outOfThresholdCount++;}}// 如果连续Z个点中有Z个点超出距离阈值X,则认为静止状态结束if (outOfThresholdCount >= Z) {break;}staticPointsSequence.push(gpsPoints[staticEndIndex]);staticEndIndex++;}const centerPoint: any = calculateGeographicalCenter(staticPointsSequence);pointsResult.finalPoints.push(centerPoint); // 将中心点加入结果数组pointsResult.stopPoints.push(centerPoint);i = staticEndIndex; // 跳到静止状态结束的点继续处理} else {pointsResult.finalPoints.push(gpsPoints[i]); // 非静止状态点,直接加入结果数组i++; // 不满足静止条件,继续检查下一个点}}return pointsResult; // 返回处理后的轨迹点数组
}function calculateDistance(point1, point2) {// 使用Haversine公式计算两个GPS点之间的距离const R = 6371e3; // 地球半径,单位为米const lat1 = point1.latitude1 * Math.PI / 180; // 第一个点的纬度转换为弧度const lat2 = point2.latitude1 * Math.PI / 180; // 第二个点的纬度转换为弧度const deltaLat = (point2.latitude1 - point1.latitude1) * Math.PI / 180; // 纬度差转换为弧度const deltaLon = (point2.longitude1 - point1.longitude1) * Math.PI / 180; // 经度差转换为弧度const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +Math.cos(lat1) * Math.cos(lat2) *Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); // Haversine公式的中间变量const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // Haversine公式的另一个中间变量return R * c; // 计算最终距离,单位为米
}function calculateGeographicalCenter(points) {if (!points || points.length === 0) {return null;}let xSum = 0;let ySum = 0;let zSum = 0;points.forEach(point => {const latRad = degreesToRadians(point.latitude1);const lonRad = degreesToRadians(point.longitude1);xSum += Math.cos(latRad) * Math.cos(lonRad);ySum += Math.cos(latRad) * Math.sin(lonRad);zSum += Math.sin(latRad);});const numPoints = points.length;const xAvg = xSum / numPoints;const yAvg = ySum / numPoints;const zAvg = zSum / numPoints;const lonAvg = Math.atan2(yAvg, xAvg);const hyp = Math.sqrt(xAvg * xAvg + yAvg * yAvg);const latAvg = Math.atan2(zAvg, hyp);return {latitude1: radiansToDegrees(latAvg),longitude1: radiansToDegrees(lonAvg)};
}function degreesToRadians(degrees) {return degrees * (Math.PI / 180);
}function radiansToDegrees(radians) {return radians * (180 / Math.PI);
}export {findStaticPoints
};

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

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

相关文章

Win11任务栏当中对 STM32CubeMX 的堆叠问题

当打开多个 CubeMX 程序的时候&#xff0c;Win11 自动将其进行了堆叠&#xff0c;这时候就无法进行预览与打开。 问题分析&#xff1a;大部分ST的工具都是基于 JDK 来进行开发的&#xff0c;Win11 将其识别成了同一个 Binary 但是实际上他们并不是同一个&#xff0c;通过配置…

redis源码分析之底层数据结构(一)-动态字符串sds

1.绪论 我们知道redis是由c语言实现的&#xff0c;c语言中是自带字符串的&#xff0c;但是为什么redis还要再实现自己的动态字符串呢&#xff0c;这种动态字符串的底层数据结构是怎样的呢?接下来我们带着这些问题来看一看redis中的动态字符串sds。 2.sds的组成 struct __at…

pico+unity3d项目配置

重点&#xff1a;unity编辑器版本要和pico的sdk要求一致、比如&#xff1a; 对于 Unity 2022.1.14 及以上版本&#xff0c;若同时在项目中使用 URP、Linear 色彩空间、四倍抗锯齿和OpenGL&#xff0c;会出现崩溃。该问题待 Unity 引擎解决。对于 Unity 2022&#xff0c;若同时…

多个版本JAVA切换(学习笔记)

多个版本JAVA切换 很多时候&#xff0c;我们电脑上会安装多个版本的java版本&#xff0c;java8&#xff0c;java11&#xff0c;java17等等&#xff0c;这时候如果想要切换java的版本&#xff0c;可以按照以下方式进行 1.检查当前版本的JAVA 同时按下 win r 可以调出运行工具…

Pytorch张量

在conda的环境中安装Jupyter及其他软件包 Pytorch 建立在张量&#xff08;tensor&#xff09;之上&#xff0c;Pytorch张量是一个 n 维数组&#xff0c;类似于 NumPy 数组。专门针对GPU设计&#xff0c;可以运行在GPU上以加快计算效率。换句话说&#xff0c;Pytorch张量是可以运…

解决QT creator中文乱码问题

1.首先设置文本编辑器为UTF-8 先在工具-选项-文本编辑器-behavior部分选择文件编码为UTF-8&#xff0c;紧接着是选择“如果编码是UTF-8则添加”&#xff0c;如下图 2.设置ext code for tools 为system 具体解决办法是 工具-选项-环境-interfaces这一栏有一个“Text code for to…

最新JJWT 0.12.6学习

本文参考的是jjwt官方github&#xff0c;链接在此&#xff0c;本文会持续跟进jjwt的最新版本 GitHub - jwtk/jjwt: Java JWT: JSON Web Token for Java and Android 简介 JJWT&#xff08;Java JWT&#xff09;是Java平台上相当流行的用于生成Json Web Token的库&#xff0c…

快捷:通过胶水语言实现工作中测试流程并行、加速

通过胶水语言实现工作中测试流程并行、加速 通过胶水语言实现工作中测试流程并行、加速工作场景&#xff08;背景&#xff09;问题抽象&#xff08;挑战&#xff09;如何做&#xff08;行动&#xff09;获得了什么&#xff08;结果&#xff09;后记相关资源 通过胶水语言实现工…

代码随想录(day6)哈希表-求两个数组的交集

什么是红黑树&#xff0c;参考链接【数据结构】史上最好理解的红黑树讲解&#xff0c;让你彻底搞懂红黑树-CSDN博客 题目&#xff1a; 注意&#xff1a;字典用{}符合、元组用()符号、列表用[]符号 table.get(num,0)1的意思 class Solution(object):def intersection(self, n…

【C++】——入门基础

文章目录 命名空间输入与输出缺省参数函数重载引用内敛函数指针控制nullptr 命名空间 当C语言遇到命名冲突时&#xff0c;很难解决&#xff0c;所以C关键字namespace就是针对这种问题的。 如何使用命名空间内的成员呢&#xff1f; 加命名空间名称及限定符&#xff08; &#…

husky 和 lint-staged 构建代码项目规范

目录 前言 最简单的方法 过 scripts 来解决如果检测工具多&#xff0c;需要多次处理 通过 husky(哈士奇)来解决容易遗忘的问题 1. 安装 2. husky init 3. 试一试​ lint-stadge 只 lint 改动的 1. 安装 2. 修改 package.json 配置 3. 添加 npm 脚本: 4.使用 Husky…

MAVSDK动态库与静态库及mavsdk_server程序macOS平台编译与安装

1.克隆mavsdk: git clone https://github.com/mavlink/MAVSDK.git --recursive 2.编译静态库 cmake -Bbuild/default -H. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF 生成makefile 生成成功,开始编译 cmake --build build/default -j8 成功生成libmavsdk.a 开…

Java之Stream流的笔记--手写版

Stream流通过讲集合或数组转换成链状流式的结构&#xff0c;简化了集合和数组进行排序、筛选、遍历、去重、统计等操作。主要包括创建流、中间操作、终结操作。若流中无终结操作&#xff0c;则中间操作不会执行&#xff1b;流是一次性的&#xff0c;使用完就会失效&#xff0c;…

vmware workstation 虚拟机安装

vmware workstation 虚拟机安装 VMware Workstation Pro是VMware&#xff08;威睿公司&#xff09;发布的一代虚拟机软件&#xff0c;中文名称一般称 为"VMware 工作站".它的主要功能是可以给用户在单一的桌面上同时运行不同的操作系统&#xff0c;它也是可进 行开发…

PDF公式转Latex

文章目录 摘要数据集 UniMER介绍下载链接 LaTeX-OCRUniMERNet安装UniMER 用的数据集介绍下载链接 PDF-Extract-Kit整体介绍效果展示评测指标布局检测公式检测公式识别 使用教程环境安装参考[模型下载](models/README.md)下载所需模型权重 在Windows上运行在macOS上运行运行提取…

MySQL:库操作

1. 创建数据库 create database [if not exists] name [create_specification], [create_specification]... []内为可选的选项 create_specification: character set charset_name -- 指定数据库采用的字符集 -- 数据库未来存储数据 collate collation_name -- 指定数据库字符…

Hi3861 OpenHarmony嵌入式应用入门--华为 IoTDA 设备接入

华为云物联网平台&#xff08;IoT 设备接入云服务&#xff09;提供海量设备的接入和管理能力&#xff0c;可以将自己的 IoT 设备 联接到华为云&#xff0c;支撑设备数据采集上云和云端下发命令给设备进行远程控制&#xff0c;配合华为云物联网平台的服 务实现设备与设备之间的控…

vue3.0 项目h5,pc端实现扫描二维码 qrcode-reader-vue3

qrcode-reader-vue3 插件简述 qrcode-reader-vue3插件&#xff0c;允许您在不离开浏览器的情况下检测和解码二维码。 &#x1f3a5; 访问设备摄像头并持续扫描传入帧。QrcodeStream&#x1f6ae; 渲染到一个空白区域&#xff0c;您可以在其中拖放要解码的图像。QrcodeDropZon…

【vivado】 管脚约束(io constraints)

一、前言 在FPGA工程中&#xff0c;所有的输入输出端口最终都要与FPGA IO 引脚进行绑定&#xff0c;需要根据需求对输入输出端口进行位置、电平标准、端口变换速率等进行约束&#xff0c;本文对io相关约束语法含义进行记录以便查询。 二、IO constrain 在vivado语言模板下可以…

MFC流的形式读取图片

1.基于对话框布置控件 2.给控件绑定变量 3.给按钮控件添加响应函数 void CMFC流的形式读取图片Dlg::OnBnClickedButton1() {// TODO: 在此添加控件通知处理程序代码//HDC mECGDC; // 设备描述表句柄//CPen mECGDrawPen; // ECG波形画笔//RECT mECGRect; // 在该矩形区域内画…