Adrian R.L.M
Todos los proyectos

Para repartidores que quieren ahorrar tiempo y combustible.

RutaFácil

PWA para repartidores: captura ubicaciones desde WhatsApp y genera la ruta de entregas más corta en el dispositivo.

Cronología2 Semanas
RolFrontend & PWA Developer
Stack TecnológicoReact 18 · Vite · TypeScript
rutafacil-ten.vercel.app
01 de 06

El Problema

Los repartidores urbanos independientes reciben decenas de pedidos diarios por WhatsApp u otras mensajerías. Coordinar el orden de visita manualmente causa pérdidas considerables de tiempo y combustible, mientras que las soluciones comerciales de ruteo implican suscripciones costosas o complejas apps con dependencias de APIs de pago.

  • Pérdida de tiempo y combustible ordenando paradas de forma intuitiva o desordenada.
  • Fricción al copiar y pegar direcciones individuales entre WhatsApp y aplicaciones de mapas.
  • Costosas suscripciones y APIs de ruteo que no son viables para trabajadores independientes o pequeños negocios.
  • Falta de funcionamiento sin conexión o consumo excesivo de datos móviles en la calle.

La Solución

Diseñé y desarrollé RutaFácil, una PWA mobile-first ultra rápida y de coste operativo $0. Permite a los repartidores compartir ubicaciones directamente desde WhatsApp al menú Compartir de su dispositivo, optimizando la ruta con algoritmos heurísticos en local.

  1. 01

    Optimización Heurística TSP

    Algoritmo de vecino más cercano con mejora 2-opt que corre en milisegundos en el navegador, minimizando la distancia total sin costes de servidor.

  2. 02

    Integración con Menú Compartir

    Uso de la Web Share Target API para recibir ubicaciones desde WhatsApp, Google Maps o Waze en un par de toques.

  3. 03

    PWA Offline-First

    Instalable como app nativa en Android/iOS con persistencia local mediante Zustand, funcionando sin interrupciones incluso sin conexión.

  4. 04

    Mapas Vectoriales $0

    Integración con MapLibre GL y teselas Esri World Street Map, ofreciendo renderizado de mapas fluido sin requerir API keys comerciales.

  5. 05

    Navegación Turn-by-Turn

    Modo guiado parada a parada con apertura directa en Google Maps/Waze y marcado de entregas con feedback auditivo y háptico.

  6. 06

    Gestión de Despachos y Cobros

    Control de montos a cobrar en efectivo por parada, generación de mensajes para clientes y exportación de rutas en formato GPX.

Stack Tecnológico

Construido con 8 tecnologías modernas, elegidas para que sea rápido, seguro y fácil de mantener.

React 18ViteTypeScriptPWAZustandMapLibre GL
Ver todo el detalle técnico

Frontend

React 18

Build Tool

Vite 6

Language

TypeScript

Mobile & Offline

PWA

Mapping / GIS

MapLibre GL

State Management

Zustand

Animations

Framer Motion

Testing

Vitest

Retos Técnicos

Algoritmo TSP On-Device a Coste Cero

Resolver el problema del viajante de comercio (TSP) sin depender de servidores o APIs de pago como Google Directions requirió implementar una solución en el cliente combinando la heurística del vecino más cercano con optimización 2-opt sobre distancias Haversine. Esta solución calcula la ruta óptima para hasta 30 paradas en menos de 5ms directamente en el smartphone del repartidor.

tsp.tstypescript
// Heurística de Vecino Más Cercano + Mejora 2-Opt (Cálculo On-Device $0)
export function optimizeOrder(
  origin: LatLng,
  stops: LatLng[],
  fixedEnd?: LatLng,
): number[] {
  const n = stops.length;
  if (n <= 1) return stops.map((_, i) => i);

  const points = fixedEnd ? [origin, ...stops, fixedEnd] : [origin, ...stops];
  const dist: number[][] = points.map((a) =>
    points.map((b) => haversineKm(a, b)),
  );
  const endIdx = fixedEnd ? n + 1 : null;

  // 1. Vecino más cercano desde el origen
  const visited = new Array<boolean>(n + 1).fill(false);
  visited[0] = true;
  const path: number[] = [0];
  let current = 0;
  for (let step = 0; step < n; step++) {
    let best = -1;
    let bestDist = Infinity;
    for (let j = 1; j <= n; j++) {
      if (!visited[j] && dist[current][j] < bestDist) {
        bestDist = dist[current][j];
        best = j;
      }
    }
    visited[best] = true;
    path.push(best);
    current = best;
  }
  if (endIdx !== null) path.push(endIdx);

  // 2. Optimización 2-opt: invierte segmentos mientras acorte el camino
  let improved = true;
  while (improved) {
    improved = false;
    for (let i = 1; i < n; i++) {
      for (let k = i + 1; k <= n; k++) {
        const a = path[i - 1];
        const b = path[i];
        const c = path[k];
        const d = k + 1 <= n ? path[k + 1] : endIdx;
        const before = dist[a][b] + (d !== null ? dist[c][d] : 0);
        const after = dist[a][c] + (d !== null ? dist[b][d] : 0);
        if (after < before - 1e-9) {
          let lo = i;
          let hi = k;
          while (lo < hi) {
            [path[lo], path[hi]] = [path[hi], path[lo]];
            lo++;
            hi--;
          }
          improved = true;
        }
      }
    }
  }

  return path.slice(1, n + 1).map((p) => p - 1);
}

Parser Multiformato y Web Share Target

Los repartidores reciben ubicaciones en formatos dispares: enlaces cortos de Google Maps, coordenadas en texto plano, enlaces de Waze o cadenas de texto complejas. Se diseñó un parser regex resiliente capaz de procesar conversaciones completas pegadas de una sola vez y extraer todas las paradas automáticamente.

¿Tienes un problema parecido?

Cuéntame cómo lo resuelves hoy y te digo qué se puede automatizar o mejorar.

Cuéntame qué te frena