Alguna vez has deseado poder previsualizar una ruta ciclista antes de recorrerla? Construimos un sistema automatizado que genera hermosos videos de sobrevuelo para cada ruta de fiesta en Party Onbici, dando a los ciclistas una vista previa cinematografica de lo que pueden esperar en su recorrido.

El Desafio

Al organizar o unirse a un evento ciclista, entender la ruta es crucial. Los mapas estaticos son utiles, pero no transmiten la experiencia de realmente recorrer la ruta. Queriamos dar a los usuarios una forma de “volar virtualmente” a traves de la ruta antes de comprometerse con un paseo.

Nuestra Solucion: Renderizado de Video Headless

Construimos un servicio Node.js que ejecuta un navegador Chrome headless usando Puppeteer, renderiza la ruta en un mapa interactivo MapLibre GL, y captura fotogramas mientras una camara virtual vuela a lo largo del camino. Asi es como funciona:

1. Interpolacion de Ruta

Una ruta ciclista puede tener miles de puntos de coordenadas. Para crear un video suave a 30 FPS durante 15 segundos, necesitamos exactamente 450 fotogramas. Usamos interpolacion basada en distancia para muestrear la ruta 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. Animacion de Camara con Orientacion Suave

Simplemente apuntar la camara en la direccion de viaje crea movimientos bruscos en rutas sinuosas. Suavizamos la orientacion de la camara usando un promedio movil de 12 fotogramas, con manejo especial para el cruce de 360 grados/0 grados:

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. Renderizado MapLibre GL

La ruta se renderiza sobre una hermosa capa base de Stadia Maps con:

  • Una linea de ruta resaltada con contorno blanco para visibilidad
  • Indicador animado de posicion de camara
  • Marcadores de inicio (verde) y fin (rojo)
  • Extrusiones opcionales de edificios 3D para areas urbanas

4. Captura de Fotogramas y Codificacion de Video

Puppeteer captura cada fotograma como una captura de pantalla PNG, luego usamos ffmpeg para codificarlos en un video WebM con codec VP9:

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

Arquitectura Tecnica

1
Django App → Celery Task → Node.js Renderer → S3 Storage
  1. Django activa la generacion de video cuando se crea o actualiza una fiesta
  2. Celery encola la tarea para procesamiento asincrono
  3. El renderizador Node.js ejecuta Puppeteer con MapLibre GL
  4. ffmpeg codifica los fotogramas capturados
  5. El video se sube a S3 y se actualiza el registro de la fiesta

Consideraciones de Rendimiento

ResolucionDuracionTiempo de RenderizadoTamano de Archivo
1280x72015s60-90s2-4 MB
1920x108015s90-120s4-8 MB

Para produccion, usamos contenedores Docker con renderizado por software (SwiftShader) para evitar dependencias de GPU:

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

Funcionalidades que Agregamos

  • Dimensiones de video personalizables - Formato cuadrado para compartir en redes sociales
  • Marca de agua con logo - Personaliza tus videos con el logo de tu organizacion
  • Inclinacion y zoom de camara - Ajusta el angulo de vision y la altitud
  • Estilo de linea de ruta - Colores y anchos personalizados

Integracion con Django

Desde el codigo de la aplicacion, generar un video es tan simple como:

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

La URL del video se actualiza automaticamente cuando el renderizado se completa:

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

Proximos Pasos

Estamos explorando varias mejoras:

  • Vista previa en tiempo real - Renderizado en vivo mientras los usuarios dibujan rutas
  • Multiples angulos de camara - Vistas laterales, tomas aereas
  • Superposicion del clima - Mostrar condiciones pronosticadas a lo largo de la ruta
  • Perfil de elevacion - Visualizar subidas y bajadas en el video

Los videos de sobrevuelo de ruta se han convertido en una de nuestras funcionalidades mas populares, ayudando a los ciclistas a tomar decisiones informadas sobre a que paseos unirse. La combinacion de tecnologias web modernas convierte lo que habria sido una tarea de produccion costosa en un servicio automatizado y bajo demanda.


Quieres probarlo? Crea una fiesta y tu video de ruta se generara automaticamente!