优化静止不动的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个点超出距离阈值X
      while (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个点超出距离阈值X
      while (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
};

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值