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

devtools/2024/9/24 10:20:13/

1.理论依据:

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

2.降噪原理

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

3.计算中心点

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

javascript">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.识别停留点序列核心代码

javascript">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.完整方法

javascript">// 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.ppmy.cn/devtools/58692.html

相关文章

若依安装和初始化教程(傻瓜教程 一步一步走)

1、若依的安装&#xff1a; 1、找到若依官方网站并选择若依的前后端分离版本 2、点击克隆 然后选择http进行复制 3、打开idea 如果有打开的项目就先关闭项目&#xff0c;然后就会自动弹出下面第二张图片&#xff0c;接着按照流程走就可以 到此若依的下载就成功了 2、若依项目…

服务器,云、边缘计算概念简单理解

目录 服务器,云、边缘计算概念简单理解 一、服务器 二、云计算 三、边缘计算

【笔记】虚拟机中的主从数据库连接实体数据库成功后的从数据库不同步问题解决方法2

错误&#xff1a; Last_Errno: 1008 Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction ANONYMOUS at source log mysql-bin.000014, end_log_pos 200275. See error lo…

Linux离线安装redis

Linux离线安装redis 把文件夹中的文件redis_6.2.6传输到系统下 或者复制到镜像下docker cp redis_6.2.6 [镜像id] /home/路径 # cd redis_6.2.6 添加执行权限 chmod x install.py 安装 ./install.py 运行 /usr/local/bin/redis-cli 重启 /usr/local/redis/redis-s…

php 中 new self() 和 new static()区别

在 PHP 中&#xff0c;new self() 和 new static() 都用于创建当前类的实例&#xff0c;但它们在运行时绑定和继承上下文中的行为有所不同。 new self() self 关键字引用的是当前类本身。无论在哪个类中调用它&#xff0c;它总是引用定义关键字的那个类。这意味着&#xff0c…

【算法笔记自学】第 8 章 提高篇(2)——搜索专题

8.1深度优先搜索&#xff08;DFS&#xff09; #include <cstdio>const int MAXN 5; int n, m, maze[MAXN][MAXN]; bool visited[MAXN][MAXN] {false}; int counter 0;const int MAXD 4; int dx[MAXD] {0, 0, 1, -1}; int dy[MAXD] {1, -1, 0, 0};bool isValid(int …

11 个例子讲清spark提交命令参数

目录 提交命名参数详情为什么有这么多参数如何开始学习一些具体的例子1. 基本的Spark应用提交2. 提交带有依赖的Python脚本3. 运行Spark SQL作业4. 提交Spark Streaming作业5. 使用外部包运行Spark作业6. 动态资源分配7. 使用多个配置文件8. GPU 支持9. 自定义日志配置10. 使用…

GitHub每日最火火火项目(7.12)

项目名称&#xff1a;public - apis / public - apis 项目介绍&#xff1a;这是一个集体列表&#xff0c;包含了各种免费的 API。该项目可能致力于收集和整理不同领域的免费 API&#xff0c;为开发者提供便利&#xff0c;使其能够更轻松地获取所需的数据和功能。通过使用这些免…