O mapa de rastreamento é a tela de maior engajamento em qualquer app de delivery de comida ou de transporte por aplicativo. É para ela que o cliente fica olhando enquanto espera. Acertar os detalhes, com movimento suave, ETAs precisos e uma linha de rota clara, é o que separa um app que passa profissionalismo de um que parece pouco confiável.
Este tutorial constrói do zero o mapa de rastreamento voltado ao cliente: um backend que transmite posições de GPS via WebSocket, um frontend que as recebe e move o marcador do motorista sem saltos, uma linha de rota vinda da Routing API do MapAtlas e uma exibição de ETA ao vivo. A implementação completa tem menos de 70 linhas de JavaScript no lado do cliente e foi pensada para se integrar a qualquer backend capaz de enviar mensagens por WebSocket.
A arquitetura serve para delivery de comida, mercado, transporte por aplicativo, serviços em campo e qualquer outro caso em que um veículo se desloca até um destino fixo enquanto um cliente acompanha tudo em tempo real.
Visão geral da arquitetura
Antes de escrever código, vale entender o fluxo de dados:
- App do motorista (celular, hardware de GPS) envia latitude/longitude para o seu backend a cada 3 a 5 segundos.
- Backend (Node.js, Python, Go, o que você preferir) persiste a última posição conhecida e a transmite via WebSocket para todos os assinantes conectados daquele pedido.
- Navegador do cliente recebe as mensagens WebSocket e move um marcador no mapa com animação interpolada.
- Routing API é chamada uma vez, quando o pedido é criado, para buscar a rota planejada. A polyline decodificada aparece como uma camada de linha.
- ETA é recalculado comparando a distância restante com a velocidade média, ou chamando de novo a Routing API a partir da posição atual do motorista.
A implementação do backend está fora do escopo deste tutorial, mas qualquer servidor WebSocket que envie mensagens neste formato funciona com o código de frontend abaixo:
{
"type": "position_update",
"orderId": "order-8821",
"lat": 52.3741,
"lng": 4.8952,
"heading": 92,
"speed": 28,
"timestamp": 1738234521000
}
Passo 1: inicializar o mapa
Configure o mapa centralizado na origem da entrega. A chamada à Routing API acontece no Passo 4, então por enquanto apenas inicialize o canvas.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'tracking-map',
style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 14
});
// Marcador de destino (restaurante ou ponto de coleta)
const destination = [4.9001, 52.3791];
new mapmetricsgl.Marker({ color: '#EF4444' })
.setLngLat(destination)
.setPopup(new mapmetricsgl.Popup().setHTML('<strong>Pickup point</strong>'))
.addTo(map);
Passo 2: marcador do motorista com rotação pela direção
Crie o marcador do motorista separadamente para poder atualizar a posição dele a cada ping de GPS. Um elemento HTML personalizado permite girar o ícone do marcador conforme a direção do motorista, um detalhe pequeno que deixa o rastreamento bem mais realista.
// Elemento personalizado para podermos girá-lo
const driverEl = document.createElement('div');
driverEl.innerHTML = '🚗';
driverEl.style.cssText = 'font-size:28px;transform-origin:center;transition:transform 0.3s';
const driverMarker = new mapmetricsgl.Marker({ element: driverEl, anchor: 'center' })
.setLngLat([4.9041, 52.3676])
.addTo(map);
function setDriverHeading(heading) {
driverEl.style.transform = `rotate(${heading}deg)`;
}
Passo 3: conexão WebSocket e interpolação suave
Este é o núcleo do mapa de rastreamento. Conectar ao WebSocket leva uma linha; a parte interessante é interpolar a posição do marcador entre os pings de GPS, para que ele deslize suavemente em vez de se teletransportar.
let prevPos = null; // { lat, lng }
let animFrame = null;
function interpolateMarker(fromLat, fromLng, toLat, toLng, durationMs) {
const startTime = performance.now();
function step(now) {
const elapsed = now - startTime;
const t = Math.min(elapsed / durationMs, 1); // 0 → 1
const lat = fromLat + (toLat - fromLat) * t;
const lng = fromLng + (toLng - fromLng) * t;
driverMarker.setLngLat([lng, lat]);
if (t < 1) {
animFrame = requestAnimationFrame(step);
}
}
if (animFrame) cancelAnimationFrame(animFrame);
animFrame = requestAnimationFrame(step);
}
const ws = new WebSocket('wss://your-backend.example.com/track/order-8821');
ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
if (msg.type !== 'position_update') return;
const { lat, lng, heading } = msg;
if (prevPos) {
// Animação suave da posição anterior até a nova
interpolateMarker(prevPos.lat, prevPos.lng, lat, lng, 400);
} else {
// Primeiro ping, posiciona o marcador imediatamente
driverMarker.setLngLat([lng, lat]);
map.flyTo({ center: [lng, lat], zoom: 15 });
}
setDriverHeading(heading);
updateETA(lat, lng);
prevPos = { lat, lng };
});
ws.addEventListener('close', () => {
console.log('Driver has arrived or connection closed.');
});
A janela de interpolação de 400 ms combina razoavelmente bem com um intervalo típico de 3 a 5 segundos entre pings de GPS: o marcador fica sempre um pouco atrás da realidade, mas nunca salta de forma perceptível.
Passo 4: desenhar a rota planejada com a Routing API
Busque a rota completa quando o pedido for atribuído. Guarde as coordenadas da polyline e desenhe-as como uma camada de linha GeoJSON. O tutorial da API de otimização de rotas cobre cenários com várias paradas; para uma entrega simples de A para B, a requisição é direta.
async function fetchAndDrawRoute(originLat, originLng, destLat, destLng) {
const url = new URL('https://gateway.mapmetrics-atlas.net/directions/');
url.searchParams.set('token', 'YOUR_API_KEY');
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: [
{ lat: originLat, lon: originLng },
{ lat: destLat, lon: destLng },
],
costing: 'auto',
format: 'osrm',
shape_format: 'geojson',
}),
});
const data = await res.json();
if (!data.routes?.length) return;
const route = data.routes[0];
map.on('load', () => {
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: route.geometry // LineString GeoJSON
}
});
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': '#3B82F6',
'line-width': 4,
'line-opacity': 0.8
}
});
});
return route.duration; // segundos
}
Passo 5: calcular e exibir o ETA
Calcule o ETA comparando a posição atual do motorista com o destino. Para mais precisão, chame de novo a Routing API a partir da posição atual do motorista a cada 30 segundos e obtenha uma estimativa de tempo de viagem atualizada.
function haversineKm(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
let lastRouteFetch = 0;
async function updateETA(driverLat, driverLng) {
const [destLng, destLat] = destination;
const distKm = haversineKm(driverLat, driverLng, destLat, destLng);
// Estimativa rápida no cliente (considera média urbana de 30 km/h)
const minutesEstimate = Math.ceil((distKm / 30) * 60);
document.getElementById('eta').textContent =
distKm < 0.1
? 'Arriving now'
: `ETA: ${minutesEstimate} min (${distKm.toFixed(1)} km)`;
// Busca de novo na Routing API a cada 30 segundos para ter mais precisão
const now = Date.now();
if (now - lastRouteFetch > 30_000) {
lastRouteFetch = now;
const url = new URL('https://gateway.mapmetrics-atlas.net/directions/');
url.searchParams.set('token', 'YOUR_API_KEY');
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: [
{ lat: driverLat, lon: driverLng },
{ lat: destLat, lon: destLng },
],
costing: 'auto',
format: 'osrm',
shape_format: 'geojson',
}),
});
const data = await res.json();
if (data.routes?.length) {
const mins = Math.ceil(data.routes[0].duration / 60);
document.getElementById('eta').textContent = `ETA: ${mins} min`;
}
}
}
Considerações de GDPR para o rastreamento de motoristas
As coordenadas de GPS de um motorista são dados pessoais nos termos do Artigo 4(1) do GDPR. Os princípios do GDPR que se aplicam aqui são a minimização de dados e a limitação da conservação, previstas no Artigo 5:
Minimização de dados: rastreie apenas os campos de que você precisa para o despacho (posição, direção e velocidade). Não registre o histórico bruto de GPS além do que for operacionalmente necessário.
Limites de retenção: dados detalhados de rastreamento da viagem devem ser apagados ou anonimizados de forma irreversível assim que o pedido for concluído. Dados de rota agregados (sem vínculo com um motorista individual) podem ser mantidos por mais tempo para otimizar a rede.
Base legal: o legítimo interesse, previsto no Artigo 6(1)(f) e sustentado por um teste de ponderação documentado, ou a execução de contrato, prevista no Artigo 6(1)(b), normalmente cobre o rastreamento de despacho em tempo real. Para qualquer uso secundário dos dados de rastreamento (analytics, benchmarking), você precisa documentar uma base separada.
Transparência com o motorista: inclua um aviso claro sobre o rastreamento no onboarding dos motoristas. Eles precisam saber o que é coletado, por quanto tempo fica armazenado e quem tem acesso.
Residência de dados: o MapAtlas processa todas as requisições de API dentro da UE. Isso elimina a preocupação com transferências para terceiros países, que surge com provedores de mapas sediados nos EUA. Veja o guia para desenvolvedores europeus sobre APIs de mapas em conformidade com o GDPR para o quadro completo de conformidade.
Para o caso de uso do setor de transporte por aplicativo e mobilidade especificamente, o MapAtlas inclui por padrão a documentação do DPA e garantias de servidores na UE. A página do setor de logística e entregas cobre cenários com frotas e vários motoristas.
Preparar para produção
Antes de liberar um recurso de rastreamento para os clientes, confira estes itens:
- Reconexão do WebSocket: adicione
ws.addEventListener('close', reconnect)com backoff exponencial. Redes móveis derrubam conexões com frequência. - Posição desatualizada: se nenhuma atualização chegar em 15 segundos, mostre um estado "localizando motorista" em vez de deixar a última posição visível.
- Detecção de chegada: quando
distKm < 0.1, dispare um estado "chegou", feche o WebSocket e mostre uma tela de confirmação. - Câmera acompanha o motorista: chame
map.panTo([lng, lat])a cada atualização de posição para manter o motorista centralizado. Ofereça um botão de "travar" para o usuário desativar o acompanhamento se quiser explorar o mapa.
Próximos passos
- Crie sua chave de API gratuita do MapAtlas e comece a construir
- Leia o tutorial da API de otimização de rotas para adicionar despacho com várias paradas ao seu app de delivery
- Explore o tutorial de mapa de imóveis para ver outro exemplo de camadas de mapa dinâmicas e orientadas por dados
Perguntas frequentes
Como mostrar a localização de um motorista se movendo de forma suave no mapa?
Os pings de GPS chegam a cada poucos segundos e criam saltos visíveis se você atualizar a posição do marcador diretamente. A interpolação suave anima o marcador entre a posição anterior e a nova durante um intervalo curto (300-500 ms), usando requestAnimationFrame para movê-lo em pequenos incrementos. Assim o movimento parece contínuo mesmo com atualizações de GPS pouco frequentes.
Os dados de localização do motorista estão sujeitos ao GDPR?
Sim. As coordenadas GPS em tempo real de um motorista são dados pessoais segundo o Artigo 4 do GDPR. As plataformas devem guardar os dados de rastreamento só pelo tempo necessário, e apagá-los ou anonimizá-los quando a viagem termina é o padrão mais seguro. O tratamento exige uma base legal (normalmente legítimo interesse para operações de despacho ou execução de contrato) e deve constar no aviso de privacidade do motorista.
Posso usar a Routing API do MapAtlas para mostrar a rota planejada no mapa de rastreamento?
Sim. Busque a rota na Routing API quando a viagem for criada, decodifique a polyline e adicione-a ao mapa como uma camada de linha GeoJSON. Conforme o motorista se move, você pode buscar a rota de novo a partir da posição atual para recalcular o ETA dinamicamente.
Perguntas frequentes
Como mostrar a localização de um motorista se movendo de forma suave no mapa?
Os pings de GPS chegam a cada poucos segundos e criam saltos visíveis se você atualizar a posição do marcador diretamente. A interpolação suave anima o marcador entre a posição anterior e a nova durante um intervalo curto (300-500 ms), usando requestAnimationFrame para movê-lo em pequenos incrementos. Assim o movimento parece contínuo mesmo com atualizações de GPS pouco frequentes.
Os dados de localização do motorista estão sujeitos ao GDPR?
Sim. As coordenadas GPS em tempo real de um motorista são dados pessoais segundo o Artigo 4 do GDPR. As plataformas devem guardar os dados de rastreamento só pelo tempo necessário, e apagá-los ou anonimizá-los quando a viagem termina é o padrão mais seguro. O tratamento exige uma base legal (normalmente legítimo interesse para operações de despacho ou execução de contrato) e deve constar no aviso de privacidade do motorista.
Posso usar a Routing API do MapAtlas para mostrar a rota planejada no mapa de rastreamento?
Sim. Busque a rota na Routing API quando a viagem for criada, decodifique a polyline e adicione-a ao mapa como uma camada de linha GeoJSON. Conforme o motorista se move, você pode buscar a rota de novo a partir da posição atual para recalcular o ETA dinamicamente.

