公司有个物流项目,要求在网页端显示司机的运输轨迹,大概思路如下,每三秒钟发送一个定位,最后把位置信息以json格式保存到数据库,在网页中显示,现在需求如下,需要把保存的点位进行纠偏,轨迹集合删除相邻的相同定位的点,并进行纠偏,由于高德地图的纠偏接口一次只支持500个点位,并且有访问次数限制,于是进行批量纠偏操作,并把纠偏的结果保存到数据库,下次再打开网页时显示已经纠偏过的轨迹
整体前台代码
$(function () {
var orderLoadList = [[${model.orderLoad}]];
//初始化地图对象,加载地图
// var position = [orderLoadList[i].varMapX, orderLoadList[i].varMapY];
var map = new AMap.Map("container", {
resizeEnable: true,
center: [orderLoadList[0].varMapX, orderLoadList[0].varMapY],
zoom: 13,
});
for (var i = 0; i < orderLoadList.length; i++) {
var icon = "";
if (i == 0) {
icon = "/content/images/load.png"
} else {
icon = "/content/images/unLoad.png"
}
var marker = new AMap.Marker({
position: [orderLoadList[i].varMapX, orderLoadList[i].varMapY], // 基点位置
title: orderLoadList[i].varName,
icon: icon,
offset: new AMap.Pixel(0, 0), // 设置点标记偏移量
anchor: 'bottom-left', // 设置锚点方位,
});
map.add(marker);
}
var isGps=[[${model.order.isGps}]];
var path=[(${model.order.varWheelPath==null?'[]':model.order.varWheelPath})];
console.log('原始path长度=='+path.length+',内容==='+JSON.stringify(path));
var isHaveCorrection=$("#isHaveCorrection").val();
var pathAfterCorrection = [(${model.order.varWheelPathAfterCorrection==null?'[]':model.order.varWheelPathAfterCorrection})];
//如果该线路已经纠偏,直接调用数据库里的纠偏后的数组进行画图
if(isHaveCorrection=='true'){
if(Array.isArray(pathAfterCorrection)){
if(pathAfterCorrection[0] && pathAfterCorrection[0] instanceof Object){
console.log('该路线已经纠偏完成,显示数据库中已经纠偏好的路线,数据库中已经纠偏的线路长度=='+pathAfterCorrection.length+',内容==='+JSON.stringify(pathAfterCorrection));
var path2=[];
for(var i =0;i<pathAfterCorrection.length;i+=1){
path2.push([pathAfterCorrection[i].x,pathAfterCorrection[i].y])
}
console.log('纠偏后保存到数据库中的路线画图:');
console.log(path2);
var polyline = new AMap.Polyline({
path: path2,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0', // 线条颜色
lineJoin: 'round', // 折线拐点连接处样式
strokeStyle: 'dashed'
});
map.add(polyline);
map.setFitView();
}
}
}else{
if (Array.isArray(path)) {
if (path[0]?.constructor === Object) {
var pathGPS=[];
var path
for(var j=0,lengt=path.length;j<lengt;j++){
pathGPS.push([path[j].x,path[j].y])
}
var pathGD=[];
function batchCorrect(path, callback) {
/*
var batchSize = 400; // 批量大小,可以根据实际情况调整
var batches = [];
for (var i = 0; i < path.length; i += batchSize) {
batches.push(path.slice(i, i + batchSize));
}*/
var zhengNum = Math.floor(path.length / 250);
var batches = splitArray(path, zhengNum);
function splitArray(array, n) {
var result = [];
if(n==0) {n=1;}
var step = Math.ceil(array.length / n);
for (var i = 0; i < n; i++) {
result.push(array.slice(i * step, (i + 1) * step));
}
return result;
}
var correctedPoints = [];
var batchIndex = 0;
function processBatch() {
if (batchIndex >= batches.length) {
console.log('批量纠偏完成');
callback(null, correctedPoints);
return;
}
var currentBatch = batches[batchIndex];
var path2 = [];
AMap.plugin('AMap.GraspRoad', function() {
var grasp = new AMap.GraspRoad();
grasp.driving(currentBatch, function(error, result) {
if (!error) {
console.log("正确入参长度=="+currentBatch.length);
correctedPoints = correctedPoints.concat(result.data.points);
console.log("纠偏后正确出参长度=="+correctedPoints.length+",内容==="+JSON.stringify(correctedPoints));
}
if(error){
console.log("错误入参长度=="+currentBatch.length+",内容==="+JSON.stringify(currentBatch));
console.log('错误编码=='+error.errcode);
console.log('错误原因=='+error.errdetail);
console.log('错误日志=='+error.errmsg);
var newPath = currentBatch;
for(var i =0;i<newPath.length;i+=1){
path2.push([newPath[i].x,newPath[i].y])
}
console.log('将无法纠偏的线路直接用黑色线条画图');
console.log(path2)
var oldLine = new AMap.Polyline({
path:path2,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0' // 线条颜色
})
map.add(oldLine);
map.setFitView();
}
batchIndex++;
processBatch();
});
});
}
processBatch();
}
if(isGps==true){
console.log('该线路的值是gps格式');
AMap.convertFrom(pathGPS, 'gps', function (status, result) {
if (result.info === 'ok') {
pathGD = result.locations; // Array.<LngLat>
for(var t=0,leng=path.length;t<leng;t++){
path[t].x=pathGD[t].lng;
path[t].y=pathGD[t].lat;
}
var path2 = [];
batchCorrect(path, function(error, correctedPath) {
if (!error) {
console.log("gps格式纠偏成功,纠偏后的路线长度=="+correctedPath.length+",内容==="+JSON.stringify(correctedPath));
// 在这里使用纠偏后的轨迹
for(var i =0;i<correctedPath.length;i+=1){
path2.push([correctedPath[i].x,correctedPath[i].y]);
}
console.log('gps格式纠偏成功,即将画图');
console.log(path2);
var polyline = new AMap.Polyline({
path: path2,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0', // 线条颜色
lineJoin: 'round', // 折线拐点连接处样式
strokeStyle: 'dashed'
});
map.add(polyline);
map.setFitView();
savaLineAfterCorrection(correctedPath);
}
});
}
});
}else{
console.log('该线路的值不是gps格式');
var path2 = [];
batchCorrect(path, function(error, correctedPath) {
if (!error) {
console.log("非gps格式纠偏成功,纠偏后的路线长度=="+correctedPath.length+",内容==="+JSON.stringify(correctedPath));
// 在这里使用纠偏后的轨迹
for(var i =0;i<correctedPath.length;i+=1){
path2.push([correctedPath[i].x,correctedPath[i].y]);
}
console.log('非gps格式纠偏成功,即将画图');
console.log(path2);
var polyline = new AMap.Polyline({
path: path2,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0', // 线条颜色
lineJoin: 'round', // 折线拐点连接处样式
strokeStyle: 'dashed'
});
map.add(polyline);
map.setFitView();
savaLineAfterCorrection(correctedPath);
}
});
}
}else{
console.log('该线路数组中的内容不是对象,原始path长度=='+path.length+',内容==='+JSON.stringify(path));
AMap.convertFrom(path, 'gps', function (status, result) {
if (result.info === 'ok') {
var lnglats = result.locations; // Array.<LngLat>
console.log('该线路数组中的内容不是对象,直接画图');
console.log(lnglats);
var polyline = new AMap.Polyline({
path: lnglats,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0', // 线条颜色
lineJoin: 'round', // 折线拐点连接处样式
strokeStyle: 'dashed'
});
map.add(polyline);
}
});
}
}
}
})
function savaLineAfterCorrection(pathAfterCorrection) {
var pathAfterCorrectionStr = JSON.stringify(pathAfterCorrection);
if(null!=pathAfterCorrectionStr && pathAfterCorrectionStr!=''){
$.ajax({
type: 'post',
url: '/transaction/updateOrderAfterCorrection',
dataType: 'Json',
data: {"id":$("#orderId").val(),
"isHaveCorrect":1,
"pathAfterCorrectionStr":pathAfterCorrectionStr},
async: false,
success: function (data) {
}
});
}
}
代码拆分
这段代码是将所有的轨迹进行拆分纠偏,代码如下,grasp.driving是纠偏方法
var originPath = [
{"x":116.478928,"y":39.997761,"sp":19,"ag":0, "tm":1478031031},
// ... 其他点
];
function batchCorrect(path, callback) {
var batchSize = 100; // 批量大小,可以根据实际情况调整
var batches = [];
for (var i = 0; i < path.length; i += batchSize) {
batches.push(path.slice(i, i + batchSize));
}
var correctedPoints = [];
var batchIndex = 0;
function processBatch() {
if (batchIndex >= batches.length) {
callback(null, correctedPoints);
return;
}
var currentBatch = batches[batchIndex];
AMap.plugin('AMap.GraspRoad', function() {
var grasp = new AMap.GraspRoad();
grasp.driving(currentBatch, function(error, result) {
if (!error) {
correctedPoints = correctedPoints.concat(result.data.points);
}
batchIndex++;
processBatch();
});
});
}
processBatch();
}
batchCorrect(originPath, function(error, correctedPath) {
if (!error) {
// 在这里使用纠偏后的轨迹
var newPath = correctedPath;
var distance = result.data.distance; // 里程
}
});
这段代码是将所有的点位进行平均分段。代码如下
var zhengNum = Math.floor(path.length / 250);
var batches = splitArray(path, zhengNum);
function splitArray(array, n) {
var result = [];
if(n==0) {n=1;}
var step = Math.ceil(array.length / n);
for (var i = 0; i < n; i++) {
result.push(array.slice(i * step, (i + 1) * step));
}
return result;
}
这段代码是将纠偏后的结果在页面进行画图
var polyline = new AMap.Polyline({
path: path2,
strokeWeight: 4, // 线条宽度,默认为 1
strokeOpacity: 1,
strokeColor: '#3780e0', // 线条颜色
lineJoin: 'round', // 折线拐点连接处样式
strokeStyle: 'dashed'
});
map.add(polyline);
map.setFitView();
这段代码是将纠偏后的结果通过ajax保存到后台数据库,代码如下
function savaLineAfterCorrection(pathAfterCorrection) {
var pathAfterCorrectionStr = JSON.stringify(pathAfterCorrection);
if(null!=pathAfterCorrectionStr && pathAfterCorrectionStr!=''){
$.ajax({
type: 'post',
url: '/transaction/updateOrderAfterCorrection',
dataType: 'Json',
data: {"id":$("#orderId").val(),
"isHaveCorrect":1,
"pathAfterCorrectionStr":pathAfterCorrectionStr},
async: false,
success: function (data) {
}
});
}
}
后台代码
轨迹集合删除相邻的相同定位的点,后台处理的逻辑如下,varWheelPath 是后台返回给前台的处理过的轨迹集合,代码如下
TOrder order = tOrderMapper.selectById(id);
Boolean isHaveCorrection = order.getIsHaveCorrection();
String varWheelPath = order.getVarWheelPath();
if(null!=varWheelPath && (isHaveCorrection==null||!isHaveCorrection)){
Gson gson = new Gson();
Type type = new TypeToken<List<Map<String, Object>>>() {}.getType();
List<Map<String, Object>> oldData = gson.fromJson(varWheelPath, type);
List<Map<String, Object>> newData = new ArrayList<Map<String, Object>>();
Stack<Map<String, Object>> stack = new Stack<Map<String, Object>>();
for (Map<String, Object> item : oldData) {
if (!stack.isEmpty() && stack.peek().get("x").toString().equals(item.get("x").toString()) && stack.peek().get("y").toString().equals(item.get("y").toString())) {
continue; // 如果栈不为空并且栈顶元素与当前元素x、y值相同,则跳过当前元素
}
stack.push(item); // 将元素压入栈中
}
for (Map<String, Object> item : stack) {
newData.add(item);
}
String varWheelPathNew =new GsonBuilder().create().toJson(newData);
order.setVarWheelPath(varWheelPathNew);
}