Ja desejou poder pre-visualizar uma rota de ciclismo antes de a percorrer? Construimos um sistema automatizado que gera belos videos de sobrevoo para cada rota de festa no Party Onbici, oferecendo aos ciclistas uma pre-visualizacao cinematografica do que esperar na sua jornada.

O Desafio

Ao organizar ou participar num evento de ciclismo, compreender a rota e crucial. Os mapas estaticos sao uteis, mas nao transmitem a experiencia de realmente percorrer a rota. Queriamos dar aos utilizadores uma forma de “sobrevoar virtualmente” a rota antes de se comprometerem com um passeio.

A Nossa Solucao: Renderizacao de Video Headless

Construimos um servico Node.js que executa um navegador Chrome headless usando Puppeteer, renderiza a rota num mapa interativo MapLibre GL e captura fotogramas enquanto uma camara virtual voa ao longo do percurso. Eis como funciona:

1. Interpolacao da Rota

Uma rota de ciclismo pode ter milhares de pontos de coordenadas. Para criar um video suave a 30 FPS durante 15 segundos, precisamos de exatamente 450 fotogramas. Usamos interpolacao baseada em distancia para amostrar a rota uniformemente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Calculate cumulative distances using Haversine formula
const distances = [0];
for (let i = 1; i < coordinates.length; i++) {
  const dist = haversineDistance(coordinates[i-1], coordinates[i]);
  totalDistance += dist;
  distances.push(totalDistance);
}

// Sample at exactly totalFrames points
for (let frame = 0; frame < totalFrames; frame++) {
  const targetDist = (frame / (totalFrames - 1)) * totalDistance;
  // Interpolate position at this distance...
}

2. Animacao da Camara com Orientacao Suave

Simplesmente apontar a camara na direcao de viagem cria movimentos bruscos em rotas sinuosas. Suavizamos a orientacao da camara usando uma media movel de 12 fotogramas, com tratamento especial para a transicao de 360 graus/0 graus:

1
2
3
4
5
6
7
// Use sin/cos components for circular averaging
for (let j = i - smoothWindow; j <= i + smoothWindow; j++) {
  const rad = rawBearings[j] * Math.PI / 180;
  sinSum += Math.sin(rad);
  cosSum += Math.cos(rad);
}
const avgBearing = Math.atan2(sinSum/count, cosSum/count) * 180 / Math.PI;

3. Renderizacao MapLibre GL

A rota e renderizada sobre uma bela camada base Stadia Maps com:

  • Uma linha de rota destacada com contorno branco para visibilidade
  • Indicador animado de posicao da camara
  • Marcadores de inicio (verde) e fim (vermelho)
  • Extrusoes opcionais de edificios 3D para areas urbanas

4. Captura de Fotogramas e Codificacao de Video

O Puppeteer captura cada fotograma como captura de ecra PNG, depois usamos ffmpeg para os codificar num video WebM com codec VP9:

1
2
3
ffmpeg -framerate 30 -i frame_%05d.png \
  -c:v libvpx-vp9 -crf 20 -b:v 0 \
  route.webm

Arquitetura Tecnica

1
Django App → Celery Task → Node.js Renderer → S3 Storage
  1. Django aciona a geracao de video quando uma festa e criada ou atualizada
  2. Celery coloca a tarefa em fila para processamento assincrono
  3. O renderizador Node.js executa Puppeteer com MapLibre GL
  4. ffmpeg codifica os fotogramas capturados
  5. O video e carregado para S3 e o registo da festa e atualizado

Consideracoes de Desempenho

ResolucaoDuracaoTempo de RenderizacaoTamanho do Ficheiro
1280x72015s60-90s2-4 MB
1920x108015s90-120s4-8 MB

Para producao, usamos contentores Docker com renderizacao por software (SwiftShader) para evitar dependencias de GPU:

1
2
--use-angle=swiftshader
--enable-unsafe-swiftshader

Funcionalidades que Adicionamos

  • Dimensoes de video personalizaveis - Formato quadrado para partilha em redes sociais
  • Marca de agua com logotipo - Personalize os seus videos com o logotipo da sua organizacao
  • Inclinacao e zoom da camara - Ajuste o angulo de visao e a altitude
  • Estilo da linha de rota - Cores e larguras personalizadas

Integracao com Django

A partir do codigo da aplicacao, gerar um video e tao simples como:

1
2
party = Party.objects.get(uid="...")
party.generate_video(width=1080, height=1080, duration=10)

O URL do video e atualizado automaticamente quando a renderizacao termina:

1
2
3
4
5
{% if party.video_url %}
<video controls>
  <source src="{{ party.video_full_url }}" type="video/webm">
</video>
{% endif %}

Proximos Passos

Estamos a explorar varias melhorias:

  • Pre-visualizacao em tempo real - Renderizacao ao vivo enquanto os utilizadores desenham rotas
  • Multiplos angulos de camara - Vistas laterais, planos aereos
  • Sobreposicao meteorologica - Mostrar condicoes previstas ao longo da rota
  • Perfil de elevacao - Visualizar subidas e descidas no video

Os videos de sobrevoo de rota tornaram-se uma das nossas funcionalidades mais populares, ajudando os ciclistas a tomar decisoes informadas sobre que passeios participar. A combinacao de tecnologias web modernas transforma o que teria sido uma tarefa de producao dispendiosa num servico automatizado e a pedido.


Quer experimentar? Crie uma festa e o seu video de rota sera gerado automaticamente!