Implementação de rastreamento de trajetória e movimento de vários veículos em tempo real com base no mapa vue + Baidu (jornada mental)

Há duas fotos no início, e o resto depende de soprar

origem.gif

202203231421Playback.gif

A realização do rastreamento de trajetória e movimento em tempo real de vários veículos com base no mapa vue+Baidu é dividido em duas partes: a primeira parte "Jornada Mental" e a próxima parte "Perspectiva de Deus". caminhar no processo de realização O desvio, a próxima parte é a implementação da versão final. Na verdade, não existe a perspectiva de Deus, só espero que um dia possamos fazer menos desvios através da revisão contínua.

Este artigo é o primeiro artigo, o artigo da jornada mental.

Antecedentes do projeto

10.000 palavras são omitidas aqui e entregues ao gerente de produto. A demanda que recebi da minha parte é, na verdade, perceber o movimento do veículo em tempo real e acompanhar a trajetória. Não, na verdade, o que eu recebi foi um código que meus companheiros tiveram que retificar! Veja como posso transformar a imagem 1 no efeito da imagem 2, hehe [ignore a imagem da camada flutuante que acidentalmente retive no canto inferior direito da tela de gravação da imagem 2]

ideias de código antigo

Dando uma olhada no código deste companheiro de equipe que nunca conheceu, de fato, sua estrutura de código original era relativamente clara.

Desenhos que podem ser mantidos:

  1. Receba dados no modo websocket. Como os dados do veículo serão gerados continuamente, não é aconselhável puxar o front-end regularmente, então o método websocket é usado para receber os dados.
  2. Marque a cobertura (marcador e etiqueta) de um carro por id, o que é conveniente para exclusão posterior
  3. Bem, eu tento pensar sobre isso, deve haver 3

pontos de melhoria

  1. O veículo está saltando para frente, muito irreal
  2. fundo muito simples

Recursos que precisam ser adicionados

  1. O veículo precisa exibir a velocidade em tempo real
  2. Uma lista precisa ser adicionada para atualizar as informações atuais do veículo em execução em tempo real, e a exibição dos primeiros 10 veículos é limitada
  3. Adicione a função de linha de trajetória para rastrear a trajetória do veículo em tempo real

O motivo do salto do veículo

A ideia de implementação original é: toda vez que os dados são recebidos, limpe a cobertura (carro e etiqueta) desenhada pelo carro, e redesenhe a cobertura com os novos dados de latitude e longitude, o veículo está diretamente do primeiro ponto para outro ponto, Então parece que está pulando.

Etapas de retrofit

A seguir estão algumas idéias que foram finalmente abandonadas no processo do projeto.Se você quiser ver o plano final diretamente, por favor, vá para a Perspectiva de Deus.

1. Redesenhar = "definir nova posição"

我的第一反应是移动位置总比重新画要快吧,所以首先从改造数据结构入手,把后端发过来的轨迹消息按照车辆归类记录下来,设置初始状态为'undraw',并在每次来一个新消息的时候,拿所有状态为'undraw'的点去移动,移动开始前把状态设为'drawing', 移动结束后把状态设为'drawed'(此处先埋一个坑,坑1)。

自认为看起来很完美,撸完后发现,设置车辆位置的时候,经常提示这个车的覆盖物还不存在。可是我明明是画完车再移动它的位置的呀,难道画车这个步骤的异步的?(此时的我还傻傻的忽视了那个大大的坐标转换函数,sigh)

异步就异步吧,再加两个状态!如果是这辆车第一个点,则画之前标记为'marking',画完后标记为'marked',只有当第一个点的状态为'marked'后,才进行后面的移动。终于不报错了,但我期待的效果是半点都没有,似乎还更槽糕了。(继续埋坑,坑2

2. 平滑效果

开始全网搜如何让车辆平滑移动:

a ) 百度地图自有的轨迹动画api参考4,更适用于已知整个轨迹,并在指定的时间内回放完成。

b) 前辈写的基于百度地图的多图标平滑移动方案参考2,主要思路是补点,根据两个点之间距离来计算要补多少个点,并用setInterval去定时移动到下一个点。但其中用到的计算距离的函数适用于百度地图jsapi v2版本,我们用的是百度地图js webgl v1版本,要注意一下不能直接使用。

c) 其他的基本上也是补点的思路,就是计算距离的函数不太一样,如参考3,是从其他文章里了解到的turfjs包,里面有很多跟地图和距离相关的工具函数,这里记录一下,以后可能用的到。

到这里基本确定使用补点的思路。最开始因为方案b)不能直接使用,采用了方案c)里的函数,发现车辆几乎都没动(其实是车已经飞走了,年少无知的我以为车没动),就把距离打印出来看了一下,看到两点之间的距离是0.00099km,想着莫非太近了,所以看起来不动?又或者是这个库的距离算起来不准,不死心地跑去百度那边试了一下,虽然大了那么一丢丢,但绝对值还是很小。

var from = turf.point([113.27720709322817, 23.351992192427748]);
var to = turf.point([113.2772194870973, 23.352001006312186]);
var options = {units: 'miles'};

turf.distance(from, to, options);
0.0009944611081986045
复制代码

这时打算先取个巧,不计算距离了,自己先固定一个分割的点数看看效果。观察后端发过来的消息,大概每个车每秒有2条数据,按照60fps来算,设置了两点之间共分成30个点来画,又信心满满地试了一把。

这时发现了一个异常,移动的时候,从一跳一跳变成了一顿一顿,这时眼瞎的我终于发现了那个不起眼的百度坐标转换函数,它不仅要调用百度地图api来拿到转换后的结果,而且限制了每次最多只能转换10个点,而我家的破网为了让我按时下班,一到晚上就卡得不行,所以这个问题被无限放大了。敢情我瞎忙活了半天,瓶颈根本不在画图上,而在调用api上。这时坑2的问题得到了解释, 异步的原因不在于画图,而在于调用api!那就先用10个点凑合一下吧,总比没有好……

3. 换皮

随着时间一分一秒地过去,我内心还是比较焦虑的,想着我改的这破玩意儿没法交差啊,为了欺骗一下自己和产品,打算先做一下改进点2,换个背景。这时的我不知道这个专业术语叫做卫星图,又是一顿全网猛搜,找到了相关的设置,其实就一句话的事。

bMap.setMapType(BMAP_EARTH_MAP)

嗯,看起来像两天工作量的样子了。【想到了换皮不换内核的浏览器们,手动狗头】

4. 本地模拟websocket数据

这时已经周五下午了,据我前两天观察,后端服务一到晚上就会关掉,这如何满足我想周末加班的欲望?不就是发个数据嘛,我也会。

步骤1:利用参考1的方案,收集了一段实时数据,保存成har文件。

步骤2:搜了下如何打开har文件,发现它本质是个json,那就好办了,把后缀改成json,观察数据。

步骤3:用websocket关键词搜索,搜到了有且仅有一个_webSocketMessages这个字段,我关心的数据都在里面。

步骤4: 提取所有接收的数据,即类型为'receive'的数据,存成json文件。

const fs = require('fs')
const path = require('path')

const input = process.argv[2]
const fullname = input.split('/').slice(-1)[0]
const filename = fullname.substring(0, fullname.lastIndexOf('.')) 
let objArr = JSON.parse(fs.readFileSync(input, 'utf8')).log.entries

const websocketReqs = objArr.filter(o => { return o._webSocketMessages && o._webSocketMessages.length > 0})
const receivedMsgs = websocketReqs.length > 0 && websocketReqs[0]._webSocketMessages.filter(item => item.type === 'receive')

if (receivedMsgs && receivedMsgs.length > 0) {
    try {
        fs.writeFileSync(path.resolve(__dirname, `./${filename}.json`), JSON.stringify(receivedMsgs, null,"\t"))
    } catch(e) {
        console.log(e)
    }
}
复制代码

json文件的格式如下:

[
	{
		"type": "receive",
		"time": 1648170807.1585,
		"opcode": 1,
		"data": "realdata1"
	},
	{
		"type": "receive",
		"time": 1648170807.329674,
		"opcode": 1,
		"data": "realdata2"
	}
]
复制代码

步骤5: 用nodejs启一个最简单的websocket后端服务,读取json文件,按照数据中的time字段作为时间间隔进行数据回放。

const realtimeTraces = JSON.parse(fs.readFileSync('./已提取的jons文件.json', 'utf8'));
const server = ws.createServer((connect) => {
  console.log(`用户链接上来了`);
  // 用户传递过来的数据,text事件就会被触发
  connect.on("text", (data) => {
    console.log(`用户传来的数据${data}`);
  });
  // 当连接断开时,就会执行这个事件 注册close事件就要注册下面的error事件
  connect.on("close", () => {
    console.log(`链接断开了`);
  });

  // 注册一个error事件,处理用户的错误信息
  connect.on("error", () => {
    console.log(`用户链接异常`);
  });

// send realtime trace
let baseTime
for(let i=0; i<realtimeTraces.length; i++) {
    const item = realtimeTraces[i]
    if (i === 0) {
        baseTime = item.time
    }
    setTimeout(() => {
        connect.send(typeof item.data === 'string' ? item.data : JSON.stringify(item.data));
      }, (item.time - baseTime)*1000);
  }
  
});
const PORT = 7777;
server.listen(PORT, () => {
  console.log(`服务启动成功,端口号${PORT}`);
});
复制代码

5. 修改绘图的触发时机

准备好后端数据后,又可以开心地研究前端实现了。(当我一遍一遍回放这段数据的时候,我想到了《开端》里的循环……)

这时我发现一个诡异的现象,车咋一瞬间铺满了屏幕,像极了当年windows中毒的感觉(不小心暴露了年龄)。作为一个密集恐惧症的我,立马关掉了页面,思考起了人生,哦不,思考起了原因。

之前的逻辑是每次收到消息的时候,去触发绘图【包括新增和移动】动作,但如果一下子涌来大量消息,就会在屏幕上堆满车(后来发现其实这个问题被放大了1000倍,因为我在分发数据的时候,setTimeout的时间忘记乘以1000了……)。

既然是接收消息的速度跟消费消息的速度不匹配,此处应该来一个消息队列。哦,对,我是前端,此处应该来一个数组,当接收消息的时候,把车的信息先缓存起来,再找机会去消费消息(埋坑3,坑3)。

哼哧哼哧改了一通,把接收消息和消费消息的函数分别写好了,消费消息那里会遍历所有车,并根据车的绘制状态进入自循环,然后我痛苦地发现我找不到一个合适的第一推动力(上帝应该不会帮我),又hack了一把,在收消息时对车计个数,当车的数量为1时,触发消费消息,作为函数调用的入口。

不管怎么样,车好歹动了起来。

6. 车辆消失后从地图上移除

开心了不到2秒,车又堆满了屏幕。

哦,该死,我只是不停地在增加车,却没有在车辆消失时把车移走。

那么问题来了,怎么判定车辆消失呢?

又观察了一下后端数据,发现很多消息是空的, 我就自己拍了个板,N条消息后如果还是没有这辆车过来,就判定它消失了。但当我不断把N调大,发现效果还是很差后,我只好承认这个算法不行。 又想了一个很损的方法,反正车在高速上,所观察的路段又很短,先假设先进先出吧,当车超过N辆后,把最先来的那辆车移除掉。

嗯,一顿操作后,我终于能正视屏幕了。

其实此时的我内心慌得一逼,已经处于代码能不能跑起来完全听天由命的状态。

7. 向大佬求助

到了周五下班的点,跟组长老实汇报了一下工作,感觉项目要失控,正常的前端听我描述的第一反应都是让后端把数据处理好给我,我只负责展示就可以了。要展示10条数据,就让后端返回10条数据,我也不用去判断车辆是不是消失,也不用自己去缓存各种信息。总之就是不用管以前的消息格式是怎么样的,只要我想好我要什么数据,自己mock好数据做好demo,让后端去适配就可以了。

我一边想果然是大佬,思路就是不一样;一边心存疑虑,想着我不知道车啥时候消失,后端拿到的数据跟我一样,他怎么会知道。

8. DIFF算法

不管怎么样,按照大佬的思路搞一波吧。先模拟了第一条10车数据的消息,开心地接收好。等模拟第二条消息的时候,发现前端这边无法一股脑替换,还是因为数据的速度跟绘制的速度并不匹配,假设直接替换,那没画完的车,那些数据就再也没有机会画上去了。

能把所有简单问题搞复杂的我此时还不死心,想到了大名鼎鼎的diff算法,根据前后两次消息进行diff,还煞有介事先写好注释。

// 新旧list对比,根据不同情况进行不同的操作
// 若新的有,旧的没有,则插入,tag记为'PLACEMENT'
// 若旧的有,新的没有,则删除,tag记为'DELETION'
// 若两者都有,则更新,tag记为'UPDATE'
复制代码

Quando terminei de marcar todos os dados a serem executados, me deparei com o problema anterior novamente. A premissa de mover o veículo é criar o veículo, e a criação do veículo é assíncrona. Ainda preciso registrar o status, para que eu possa não usar o backend para retornar dados diretamente, o plano falhou.

9. Simplifique o problema da perspectiva de um carro

Quando penso em todos os carros juntos, é muito difícil localizar o problema. Foi quando percebi porque não comecei com um carro.

Para um carro, de fato, os requisitos são muito claros:

  1. receber mensagem
  2. desenho de carro
  3. carro móvel
  4. remover carro

Neste momento, a ideia de repente ficou clara e cada etapa pode ser implementada de forma independente.

  1. Quando o back-end envia uma mensagem, ele recebe a mensagem.
  2. Ao receber a mensagem, determina-se se é um carro novo e, se for um carro novo, o carro é sorteado.
  3. Após desenhar o carro, se ainda houver pontos não pintados, mova o carro. Retire dois pontos de cada vez como o ponto inicial e o ponto final e mova-se pelo algoritmo do ponto complementar.
  4. Se ainda não houver nenhum ponto sem pintura (predefinido 3 segundos), determina-se que o veículo desaparece e o veículo é removido. Quando implementado, a contagem regressiva é reiniciada toda vez que um novo ponto é desenhado.

De repente, descobri que essa ideia não tem mais os problemas originais, nem precisa pensar no problema da primeira força motriz (poço 3), nem adicionar os estados 'marcação' e 'marcado' ao primeiro ponto para saber quando o o carro pode ser movido (Pit 2), e o algoritmo de desaparecimento do veículo não é mais o gato de Schrödinger, e pode ser ajustado dinamicamente de acordo com a frequência dos dados reais coletados. Além disso, costumava levar um lote de pontos (pit 1) para operar, mas agora tomo dois pontos de cada vez, e o desempenho é mais estável.

Até agora, a maioria dos desvios foram concluídos, e a realização do movimento em tempo real de vários veículos e rastreamento de trajetória com base no mapa vue + Baidu (Perspectiva de Deus) abrirá o compartilhamento de tecnologia real.

referências

1. Use o Chrome para salvar e visualizar solicitações de rede

2. Os multi-ícones do Baidu se movem suavemente

3. Movimento suave de polilinhas

4. Animação de trajetória

Acho que você gosta

Origin juejin.im/post/7079366814752309278
Recomendado
Clasificación