O mapa de rastreamento é a tela de maior engajamento em qualquer app de entrega de comida ou compartilhamento de caronas. É o que os clientes observam enquanto esperam. Acertar, movimento suave, ETAs precisos, uma linha de rota clara, é a diferenca entre um app que parece profissional e um que parece pouco confiável.
Este tutorial constrói o mapa de rastreamento voltado para o cliente desde os primeiros princípios: um backend que transmite posicoes GPS via WebSocket, um frontend que as recebe e move um marcador de motorista sem pulos, uma linha de rota da API MapAtlas Routing, e uma exibicao de ETA ao vivo. A implementacao completa tem menos de 70 linhas de JavaScript do lado do cliente, projetada para integrar-se com qualquer backend que possa enviar mensagens WebSocket.
A arquitetura funciona para entrega de comida, mantimentos, compartilhamento de caronas, servicos de campo e qualquer outro caso de uso em que um veículo se move em direcao a um destino fixo e um cliente o observa acontecendo em tempo real.
Visao geral da arquitetura
Antes de escrever código, é útil entender o fluxo de dados:
- App do motorista (mobile, hardware GPS) envia latitude/longitude para seu backend a cada 3-5 segundos.
- Backend (Node.js, Python, Go, sua escolha) persiste a última posicao conhecida e a transmite via WebSocket para todos os assinantes de pedidos conectados.
- Navegador do cliente recebe mensagens WebSocket e move um marcador no mapa usando animacao interpolada.
- API de roteamento é chamada uma vez quando o pedido é criado para buscar a rota planejada. A polilinha decodificada é exibida como uma camada de linha.
- ETA é recalculado comparando a distância restante com a velocidade média, ou chamando novamente a API de roteamento da posicao atual do motorista.
A implementacao de 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: Inicializacao do mapa
Configure o mapa centralizado na origem da entrega. A chamada da API de roteamento acontece na etapa 4, então por enquanto apenas inicialize a tela.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'tracking-map',
style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 14
});
// Destination marker (restaurant or pickup point)
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 de motorista com rotacao de direcao
Crie o marcador do motorista separadamente para que você possa atualizar sua posicao em cada sinal GPS. Um elemento HTML personalizado permite que você rode o ícone do marcador para refletir a direcao do motorista, um pequeno detalhe que torna o rastreamento muito mais realista.
// Custom element so we can rotate it
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: Conexao WebSocket e interpolacao suave
Este é o núcleo do mapa de rastreamento. Conectar-se ao WebSocket é uma linha; a parte interessante é interpolar a posicao do marcador entre sinais GPS para que ele deslize suavemente em vez de teleportar.
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) {
// Animacao suave da posicao anterior para a nova
interpolateMarker(prevPos.lat, prevPos.lng, lat, lng, 400);
} else {
// Primeiro sinal, coloque 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('Motorista chegou ou conexao foi fechada.');
});
A janela de interpolacao de 400ms corresponde bem a um intervalo típico de sinal GPS de 3-5 segundos, o marcador está sempre ligeiramente atras da realidade mas nunca pula notavelmente.
Passo 4: desenhe a rota planejada da API de roteamento
Busque a rota completa quando o pedido for atribuído. Armazene as coordenadas da polilinha e desenhe-as como uma camada de linha GeoJSON. O tutorial da API de otimizacao de rota cobre cenários com múltiplas paradas; para uma entrega simples de A para B o pedido é direto.
async function fetchAndDrawRoute(originLat, originLng, destLat, destLng) {
const url = new URL('https://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${originLat},${originLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
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 // GeoJSON LineString
}
});
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; // seconds
}
Passo 5: calculo e exibicao de ETA
Calcule o ETA comparando a posicao atual do motorista com o destino. Para alta precisao, chame novamente a API de roteamento da posicao atual do motorista a cada 30 segundos para obter 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);
// Fast client-side estimate (assume 30 km/h urban average)
const minutesEstimate = Math.ceil((distKm / 30) * 60);
document.getElementById('eta').textContent =
distKm < 0.1
? 'Arriving now'
: `ETA: ${minutesEstimate} min (${distKm.toFixed(1)} km)`;
// Re-fetch from Routing API every 30 seconds for accuracy
const now = Date.now();
if (now - lastRouteFetch > 30_000) {
lastRouteFetch = now;
const url = new URL('https://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${driverLat},${driverLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
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`;
}
}
}
Consideracoes de GDPR para rastreamento de motorista
As coordenadas GPS do motorista são dados pessoais sob o artigo 4(1) do GDPR. Os regulamentos que governam as plataformas de entrega de comida e compartilhamento de caronas da UE sobre este ponto nao sao ambíguos:
Minimizacao de dados: rastreie apenas os campos que você precisa para despacho, posicao, direcao, velocidade. Nao registre o histórico GPS bruto além do que é operacionalmente necessário.
Limites de retencao: dados granulares de rastreamento de viagem devem ser excluídos ou anonimizados irreversivelmente assim que o pedido for concluído. Dados de rota agregados (sem vincular novamente a um motorista individual) podem ser retidos mais por otimizacao de rede.
Base legal: interesse legítimo sob o artigo 6(1)(f) cobre rastreamento de despacho em tempo real. Para qualquer uso secundário de dados de rastreamento (análise, benchmarking), você precisa documentar uma base separada.
Transparência do motorista: incluir divulgacao clara de rastreamento no treinamento do motorista. Os motoristas devem ser informados sobre o que é coletado, por quanto tempo é retido e quem pode acessá-lo.
Residência de dados: MapAtlas processa todas as solicitacoes de API dentro da UE. Isto elimina a preocupacao de transferência de terceiros que surge com provedores de mapeamento baseados nos EUA. Veja o Guia do desenvolvedor da UE para APIs de mapa em conformidade com GDPR para o quadro completo de conformidade.
Para o caso de uso da indústria de compartilhamento de caronas e mobilidade especificamente, o MapAtlas inclui documentacao DPA e garantias de servidor da UE como padrão. A página da indústria logística e entrega cobre cenários de frota e múltiplos motoristas.
Endurecimento de producao
Antes de enviar um recurso de rastreamento para clientes, verifique esses itens:
- Reconexao de WebSocket: adicione
ws.addEventListener('close', reconnect)com regressao exponencial. As redes móveis descartam conexoes frequentemente. - Manipulacao de posicao obsoleta: se nenhuma atualizacao chegar por 15 segundos, mostre um estado "localizando motorista" em vez de deixar a última posicao visível.
- Deteccao de chegada: quando
distKm < 0.1, dispare um estado "chegou", feche o WebSocket e mostre uma tela de confirmacao. - Câmera segue motorista: chame
map.panTo([lng, lat])em cada atualizacao de posicao para manter o motorista centralizado. Dê aos usuários um botao de alternancia "bloquear" para desativar o modo de seguimento se quiserem explorar o mapa.
Próximas etapas
- Inscreva-se para sua chave da API MapAtlas gratuita e comece a construir
- Leia o tutorial da API de otimizacao de rota para adicionar despacho em múltiplas paradas ao seu app de entrega
- Explore o tutorial do Mapa de propriedade imobiliária para outro exemplo de camadas de mapa dinâmicas e orientadas a dados
Frequently Asked Questions
How do I show a driver's location moving smoothly on a map?
GPS pings arrive every few seconds, creating visible jumps if you update the marker position directly. Smooth interpolation animates the marker between the previous position and the new one over a short duration (300–500ms), using requestAnimationFrame to move the marker in small increments. This gives the appearance of continuous movement even with infrequent GPS updates.
Is driver location data subject to GDPR?
Yes. A driver's real-time GPS coordinates are personal data under GDPR Article 4. EU food delivery and rideshare platforms must minimise retention, tracking data should be deleted or anonymised once the trip is complete. Processing requires a legal basis and must be disclosed in the driver's privacy notice.
Can I use the MapAtlas Routing API to show the planned route on the tracking map?
Yes. Fetch the route from the Routing API when a trip is created, decode the polyline, and add it as a GeoJSON line layer on the map. As the driver moves, you can optionally re-fetch the route from the current position to recalculate the ETA dynamically.
