- 作者:老汪软件技巧
- 发表时间:2024-11-28 21:07
- 浏览量:
最近做了一个项目,有一个需求要展示人员轨迹,定位数据是高德的,每分钟定位一次。开发过程中,后端负责数据接收入库,前端调后端接口查点位数据,再用高德轨迹功能绘制轨迹。完事部署到测试环境发现,只有轨迹中间的线贴合人员实际运动情况,轨迹两端带有很多尖刺形状,像高中生物课本上的神经末梢,枝枝杈杈不美观,没办法给客户交差。
从网上的查信息结合定位测试人员活动情况,了解到人员在静止状态时,GPS定位漂移情况会比较严重,运动过程中的点位倒没什么问题。先去看了高德开放平台的轨迹纠正功能,需要每个定位点的经纬度、速度和方向,我们没有速度和方向,用不了。这时候没办法回想起了大学时玩过的平移居中平均法算法,用窗口范围内点位经纬度求平均值作为当前点位的经纬度值,去平滑异常定位数据,实现很简单,样例代码如下
public static void applyCenteredMovingAverage(List data, int window) {
if (window % 2 == 1) {
throw new IllegalArgumentException("窗口大小必须是奇数");
}
int halfWindow = window / 2;
// 遍历数据,从能够完整包含窗口的位置开始到结束
for (int i = halfWindow; i < data.size() - halfWindow; i++) {
double laSum = 0.000000;
double lonSum = 0.000000;
for (int j = -halfWindow; j <= halfWindow; j++) {
laSum += data.get(i + j).getLatitude();
lonSum += data.get(i + j).getLongitude();
}
data.get(i).setLocation(df.format(lonSum / (window + 1)) + "," + df.format(laSum / (window + 1)));
}
}
跑出来的结果不是很理想,轨迹毛刺长度有所缓解,但还是枝枝杈杈,并且原本带有转弯的轨迹被算平均后,转弯的直角变成了倒圆角,异常数据没处理好,正常数据给搞乱了,不适合用来处理我的数据,遂舍弃。
后面盯着轨迹两端观察,觉得这个尖尖点位应该算作错误定位,不应该想着去缓解尖刺,而是应该舍弃,然后毛刺那个尖尖的点位左右两侧的直线,如果用向量表示的话,向量夹角是个钝角,大概在135°,可以用这个来判断异常点位,样例代码如下:
public static List applyVectorAngle(List data) {
for (int i = 1; i < data.size() - 1; i++) {
double[] v1 = new double[]{data.get(i).getLongitude() - findPre(data, i).getLongitude(), data.get(i).getLatitude() - findPre(data, i).getLatitude()};
double[] v2 = new double[]{data.get(i + 1).getLongitude() - data.get(i).getLongitude(), data.get(i + 1).getLatitude() - data.get(i).getLatitude()};
Double angle = angleBetweenVectors(v1, v2);
if (angle.isNaN() || angle > 135) {
data.get(i).setFlag(false);
}
}
return data;
}
//寻找前一个点位
private static DeviceLocationRecord findPre(List data, int currentIndex) {
DeviceLocationRecord pre = data.get(currentIndex - 1);
while (!pre.isFlag()) {
currentIndex--;

if (currentIndex < 1) {
break;
}
pre = data.get(currentIndex - 1);
}
return pre;
}
// 计算两个向量之间的夹角(返回角度制)
private static double angleBetweenVectors(double[] v1, double[] v2) {
double dotProduct = dotProduct(v1, v2);
double normV1 = norm(v1);
double normV2 = norm(v2);
double angle = Math.acos(dotProduct / (normV1 * normV2));
return Math.toDegrees(angle); // 转换为角度制
}
// 计算向量的范数(L2范数)
private static double norm(double[] v) {
return Math.sqrt(dotProduct(v, v));
}
// 计算两个向量的点积
private static double dotProduct(double[] v1, double[] v2) {
if (v1.length != v2.length) {
throw new IllegalArgumentException("向量长度不相等");
}
double product = 0.0;
for (int i = 0; i < v1.length; i++) {
product += v1[i] * v2[i];
}
return product;
}
定位数据经此处理后,过滤掉flag为false的返回给前端,效果很好,基本没影响到正常轨迹,且全部删掉了尖刺定位,堪称完美。这段代码中有个角度参数135,大家使用的过程中可结合自己的数据情况做修改。
以上,希望对大家有所帮助。