# Fase 3: Analytics y Exploración Avanzada ## Estado: ✅ IMPLEMENTADO (Backend) La Fase 3 añade capacidades avanzadas de análisis, exploración de rutas, planificación de viajes y exportación de datos. --- ## ✨ Características Implementadas ### Backend #### Base de Datos - ✅ Migración V6: Vistas materializadas para analytics - ✅ Funciones para heatmaps, estadísticas y análisis - ✅ Tablas para cache de exportaciones - ✅ Vistas de sistema: traffic_by_hour, traffic_by_route, daily_statistics, route_performance #### Workers - ✅ Analytics Refresher: Refresco automático de vistas materializadas cada 15 minutos #### API REST - ✅ Analytics API (`/analytics`) - Heatmaps, estadísticas, performance - ✅ Explorer API (`/explorer`) - Explorador de rutas, planificador de viajes, búsqueda ### Frontend - ⏳ Componentes de Analytics (pendiente) - ⏳ Heatmap de tráfico (pendiente) - ⏳ Dashboard de estadísticas (pendiente) - ⏳ Planificador de viajes UI (pendiente) --- ## 📁 Nuevos Archivos Phase 3 ### Base de Datos ``` database/migrations/ └── V6__analytics_and_statistics.sql # Vistas y funciones de analytics ``` ### Backend Workers ``` backend/src/worker/ └── analytics-refresher.js # Refresco de vistas materializadas ``` ### Backend API ``` backend/src/api/routes/ ├── analytics.js # Endpoints de analytics y exportación └── explorer.js # Explorador de rutas y planificador ``` --- ## 🚀 Ejecutar con Fase 3 ### Usando Docker Compose ```bash # 1. Ejecutar migraciones (incluye V6) make migrate # 2. Iniciar todos los servicios (incluye analytics-refresher) make start # El worker analytics-refresher se inicia automáticamente # y refresca las vistas cada 15 minutos ``` ### Desarrollo Local ```bash cd backend # Terminal 1: Analytics Refresher npm run dev:analytics # Terminal 2: API Server npm run dev ``` --- ## 📡 Nuevos Endpoints API ### Analytics - Traffic #### GET /analytics/traffic/heatmap Obtener datos de heatmap de tráfico. **Query Parameters:** - `start_date` (opcional): Fecha inicio (ISO 8601) - `end_date` (opcional): Fecha fin (ISO 8601) - `grid_size` (opcional): Tamaño de celda en grados (default: 0.1 ≈ 11km) **Ejemplo:** ```bash # Heatmap últimos 7 días curl http://localhost:3000/analytics/traffic/heatmap # Heatmap con grid más fino curl http://localhost:3000/analytics/traffic/heatmap?grid_size=0.05 # Rango personalizado curl "http://localhost:3000/analytics/traffic/heatmap?start_date=2025-11-20T00:00:00Z&end_date=2025-11-27T23:59:59Z" ``` **Respuesta:** ```json [ { "lat": 40.4, "lon": -3.7, "intensity": 125, "avgSpeed": 78.5 }, { "lat": 41.3, "lon": 2.1, "intensity": 98, "avgSpeed": 65.3 } ] ``` #### GET /analytics/traffic/hourly Obtener patrón de tráfico por hora del día. **Query Parameters:** - `days` (opcional): Número de días para analizar (default: 7) **Ejemplo:** ```bash curl http://localhost:3000/analytics/traffic/hourly?days=30 ``` **Respuesta:** ```json [ { "hour_of_day": 0, "avg_trains": 15.3, "avg_speed": 45.2, "total_observations": 45230 }, { "hour_of_day": 7, "avg_trains": 87.5, "avg_speed": 68.7, "total_observations": 125890 } ] ``` #### GET /analytics/traffic/by-hour Obtener estadísticas de tráfico agregadas por hora. **Query Parameters:** - `limit` (opcional): Número de horas (default: 24) **Ejemplo:** ```bash curl http://localhost:3000/analytics/traffic/by-hour?limit=48 ``` #### GET /analytics/traffic/by-route Obtener estadísticas de tráfico por ruta. **Ejemplo:** ```bash curl http://localhost:3000/analytics/traffic/by-route ``` **Respuesta:** ```json [ { "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "route_type": "HIGH_SPEED", "total_trains": 234, "active_days": 30, "avg_speed": 185.4, "total_positions": 125890 } ] ``` --- ### Analytics - Statistics #### GET /analytics/statistics/daily Obtener estadísticas diarias del sistema. **Query Parameters:** - `days` (opcional): Número de días (default: 30) **Ejemplo:** ```bash curl http://localhost:3000/analytics/statistics/daily?days=90 ``` **Respuesta:** ```json [ { "date": "2025-11-27", "unique_trains": 456, "total_positions": 65432, "avg_speed": 72.3, "stopped_count": 12890, "moving_count": 52542 } ] ``` #### GET /analytics/statistics/system Obtener estado actual del sistema. **Ejemplo:** ```bash curl http://localhost:3000/analytics/statistics/system ``` **Respuesta:** ```json { "active_trains": 234, "active_alerts": 5, "delayed_trips": 12, "avg_delay_seconds": 450, "active_routes": 45, "last_update": "2025-11-27T15:30:00Z" } ``` --- ### Analytics - Performance #### GET /analytics/performance/routes Obtener métricas de rendimiento de rutas (puntualidad, retrasos). **Query Parameters:** - `limit` (opcional): Número de rutas (default: 20) **Ejemplo:** ```bash curl http://localhost:3000/analytics/performance/routes?limit=10 ``` **Respuesta:** ```json [ { "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "total_trips": 345, "delayed_trips": 23, "on_time_trips": 322, "avg_delay_seconds": 180, "median_delay_seconds": 120, "max_delay_seconds": 1800, "punctuality_percentage": 93.33 } ] ``` #### GET /analytics/performance/route/:routeId Obtener estadísticas detalladas de una ruta específica. **Query Parameters:** - `days` (opcional): Número de días para analizar (default: 7) **Ejemplo:** ```bash curl http://localhost:3000/analytics/performance/route/AVE-MAD-BCN?days=30 ``` **Respuesta:** ```json { "total_trips": 234, "unique_trains": 45, "avg_speed": 185.4, "max_speed": 298.7, "total_distance_km": 98765.4, "avg_delay_seconds": 120, "on_time_percentage": 92.5 } ``` --- ### Analytics - Delays #### GET /analytics/delays/top-routes Obtener rutas con más retrasos. **Ejemplo:** ```bash curl http://localhost:3000/analytics/delays/top-routes ``` **Respuesta:** ```json [ { "route_id": "MD-MAD-VAL", "route_name": "Madrid - Valencia", "delayed_count": 45, "avg_delay": 600, "max_delay": 2400 } ] ``` --- ### Analytics - Stations #### GET /analytics/stations/busiest Obtener estaciones más transitadas. **Query Parameters:** - `limit` (opcional): Número de estaciones (default: 20) **Ejemplo:** ```bash curl http://localhost:3000/analytics/stations/busiest?limit=10 ``` **Respuesta:** ```json [ { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_name": "Madrid Puerta de Atocha", "daily_trips": 456, "routes_count": 34 } ] ``` #### GET /analytics/stations/:stationId/statistics Obtener estadísticas de una estación específica. **Query Parameters:** - `days` (opcional): Número de días (default: 7) **Ejemplo:** ```bash curl http://localhost:3000/analytics/stations/MADRID-PUERTA-DE-ATOCHA/statistics?days=30 ``` **Respuesta:** ```json { "total_departures": 3450, "total_arrivals": 3420, "unique_routes": 45, "avg_delay_minutes": 2.5, "busiest_hour": 8 } ``` --- ### Analytics - Trains #### GET /analytics/trains/:trainId/distance Calcular distancia recorrida por un tren. **Query Parameters:** - `start_time` (opcional): Timestamp inicio - `end_time` (opcional): Timestamp fin **Ejemplo:** ```bash curl "http://localhost:3000/analytics/trains/12345/distance?start_time=2025-11-27T00:00:00Z&end_time=2025-11-27T23:59:59Z" ``` **Respuesta:** ```json { "train_id": "12345", "start_time": "2025-11-27T00:00:00Z", "end_time": "2025-11-27T23:59:59Z", "distance_km": 1234.56 } ``` --- ### Analytics - Export #### GET /analytics/export Exportar datos en diferentes formatos. **Query Parameters:** - `table` (requerido): Tabla a exportar - `format` (opcional): json, csv, geojson (default: json) - `start_date` (opcional): Fecha inicio - `end_date` (opcional): Fecha fin - `limit` (opcional): Límite de registros (default: 1000) **Tablas permitidas:** - train_positions - trains - routes - stations - alerts - trip_updates - traffic_by_hour - daily_statistics **Ejemplo JSON:** ```bash curl "http://localhost:3000/analytics/export?table=trains&format=json" ``` **Ejemplo CSV:** ```bash curl "http://localhost:3000/analytics/export?table=train_positions&format=csv&limit=5000" > positions.csv ``` **Ejemplo GeoJSON:** ```bash curl "http://localhost:3000/analytics/export?table=stations&format=geojson" > stations.geojson ``` --- ### Analytics - Refresh #### POST /analytics/refresh Refrescar manualmente las vistas materializadas. **Ejemplo:** ```bash curl -X POST http://localhost:3000/analytics/refresh ``` **Respuesta:** ```json { "success": true, "message": "Analytics views refreshed successfully", "timestamp": "2025-11-27T15:30:00Z" } ``` --- ## 📍 Explorer - Route Explorer ### GET /explorer/routes/:routeId Obtener información completa de una ruta (trips, stops, shape). **Ejemplo:** ```bash curl http://localhost:3000/explorer/routes/AVE-MAD-BCN ``` **Respuesta:** ```json { "route": { "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "route_type": "HIGH_SPEED" }, "trips": [ { "trip_id": "trip_001", "trip_headsign": "Barcelona Sants", "direction_id": 0 } ], "stops": [ { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_name": "Madrid Puerta de Atocha", "stop_lat": 40.4067, "stop_lon": -3.6906 } ], "shape": { "shape_id": "shape_001", "points": [ { "lat": 40.4067, "lon": -3.6906, "sequence": 1, "distance": 0 } ] }, "total_trips": 45, "total_stops": 8 } ``` ### GET /explorer/trips/:tripId/schedule Obtener horario completo de un viaje. **Ejemplo:** ```bash curl http://localhost:3000/explorer/trips/trip_12345/schedule ``` **Respuesta:** ```json [ { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_name": "Madrid Puerta de Atocha", "arrival_time": "08:00:00", "departure_time": "08:00:00", "stop_sequence": 1 }, { "stop_id": "BARCELONA-SANTS", "stop_name": "Barcelona Sants", "arrival_time": "10:45:00", "departure_time": "10:45:00", "stop_sequence": 3 } ] ``` --- ## 🚉 Explorer - Stations ### GET /explorer/stations/:stationId Obtener información completa de una estación. **Ejemplo:** ```bash curl http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA ``` **Respuesta:** ```json { "station": { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_name": "Madrid Puerta de Atocha", "stop_lat": 40.4067, "stop_lon": -3.6906 }, "next_departures": [ { "trip_id": "trip_001", "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "headsign": "Barcelona Sants", "scheduled_departure": "15:00:00", "estimated_delay": 180, "status": "DELAYED" } ], "routes": [ { "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona" } ], "statistics": { "total_departures": 456, "total_arrivals": 450, "unique_routes": 34, "avg_delay_minutes": 2.5, "busiest_hour": 8 } } ``` ### GET /explorer/stations/:stationId/nearby Obtener estaciones cercanas. **Query Parameters:** - `radius` (opcional): Radio en km (default: 5) **Ejemplo:** ```bash curl "http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA/nearby?radius=10" ``` **Respuesta:** ```json [ { "stop_id": "MADRID-CHAMARTIN", "stop_name": "Madrid Chamartín", "stop_lat": 40.4728, "stop_lon": -3.6797, "distance_km": 7.8 } ] ``` --- ## 🗺️ Explorer - Trip Planner ### GET /explorer/planner Planificador de viajes entre dos estaciones. **Query Parameters:** - `origin` (requerido): ID de estación origen - `destination` (requerido): ID de estación destino - `time` (opcional): Hora de salida (HH:MM:SS) - `date` (opcional): Fecha (YYYY-MM-DD) **Ejemplo:** ```bash curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS&time=08:00:00" ``` **Respuesta:** ```json { "origin": "MADRID-PUERTA-DE-ATOCHA", "destination": "BARCELONA-SANTS", "requested_time": "08:00:00", "requested_date": "today", "direct_trips": [ { "trip_id": "trip_001", "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "trip_headsign": "Barcelona Sants", "origin_departure": "08:00:00", "destination_arrival": "10:45:00", "duration_minutes": 165, "delay": { "delay_seconds": 180, "schedule_relationship": "SCHEDULED" } } ], "trips_with_transfer": [ { "trip1_id": "trip_002", "route1_name": "Madrid - Zaragoza", "trip2_id": "trip_003", "route2_name": "Zaragoza - Barcelona", "origin_departure": "08:30:00", "transfer_arrival": "09:50:00", "transfer_departure": "10:10:00", "destination_arrival": "11:30:00", "transfer_station": "ZARAGOZA-DELICIAS", "transfer_station_name": "Zaragoza Delicias", "total_duration_minutes": 180 } ], "total_options": 3 } ``` ### GET /explorer/routes/between Encontrar rutas que conectan dos estaciones. **Query Parameters:** - `origin` (requerido): ID de estación origen - `destination` (requerido): ID de estación destino **Ejemplo:** ```bash curl "http://localhost:3000/explorer/routes/between?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS" ``` **Respuesta:** ```json [ { "route_id": "AVE-MAD-BCN", "route_name": "Madrid - Barcelona", "route_type": "HIGH_SPEED", "route_color": "FF6600", "daily_trips": 25 } ] ``` --- ## 🔍 Explorer - Search ### GET /explorer/search Buscar estaciones por nombre. **Query Parameters:** - `query` (requerido): Término de búsqueda (mínimo 2 caracteres) - `limit` (opcional): Número de resultados (default: 10) **Ejemplo:** ```bash curl "http://localhost:3000/explorer/search?query=madrid&limit=5" ``` **Respuesta:** ```json [ { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_name": "Madrid Puerta de Atocha", "stop_lat": 40.4067, "stop_lon": -3.6906, "location_type": 1, "parent_station": null }, { "stop_id": "MADRID-CHAMARTIN", "stop_name": "Madrid Chamartín", "stop_lat": 40.4728, "stop_lon": -3.6797, "location_type": 1, "parent_station": null } ] ``` --- ## 🗄️ Vistas Materializadas ### traffic_by_hour Tráfico agregado por hora (últimos 30 días). **Columnas:** - hour: Hora (timestamp) - active_trains: Número de trenes activos - total_positions: Total de posiciones registradas - avg_speed: Velocidad promedio - median_speed: Velocidad mediana - max_speed: Velocidad máxima ### traffic_by_route Tráfico por ruta (últimos 30 días). **Columnas:** - route_id, route_name, route_type - total_trains: Total de trenes diferentes - active_days: Días con actividad - avg_speed: Velocidad promedio - total_positions: Posiciones registradas ### daily_statistics Estadísticas diarias del sistema (últimos 90 días). **Columnas:** - date: Fecha - unique_trains: Trenes únicos - total_positions: Posiciones totales - avg_speed: Velocidad promedio - stopped_count: Posiciones paradas - moving_count: Posiciones en movimiento ### route_performance Rendimiento y puntualidad por ruta (últimos 30 días). **Columnas:** - route_id, route_name - total_trips: Total de viajes - delayed_trips: Viajes retrasados - on_time_trips: Viajes puntuales - avg_delay_seconds: Retraso promedio - median_delay_seconds: Retraso mediano - max_delay_seconds: Retraso máximo - punctuality_percentage: % de puntualidad **Refresco:** Cada 15 minutos automáticamente via analytics-refresher worker. --- ## 🧪 Probar Phase 3 ### 1. Verificar Analytics Refresher ```bash # Ver logs del worker docker-compose logs -f analytics-refresher # Deberías ver cada 15 minutos: # "Starting analytics views refresh..." # "Analytics views refreshed successfully" ``` ### 2. Probar Analytics API ```bash # Sistema general curl http://localhost:3000/analytics/statistics/system | jq # Heatmap curl http://localhost:3000/analytics/traffic/heatmap | jq # Performance de rutas curl http://localhost:3000/analytics/performance/routes | jq # Rutas más retrasadas curl http://localhost:3000/analytics/delays/top-routes | jq # Estaciones más transitadas curl http://localhost:3000/analytics/stations/busiest | jq ``` ### 3. Probar Explorer API ```bash # Buscar estación curl "http://localhost:3000/explorer/search?query=madrid" | jq # Info de estación curl http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA | jq # Info de ruta curl http://localhost:3000/explorer/routes/AVE-MAD-BCN | jq # Planificar viaje curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS" | jq ``` ### 4. Probar Exportación ```bash # Exportar a CSV curl "http://localhost:3000/analytics/export?table=routes&format=csv" > routes.csv # Exportar a GeoJSON curl "http://localhost:3000/analytics/export?table=stations&format=geojson" > stations.geojson # Exportar posiciones filtradas curl "http://localhost:3000/analytics/export?table=train_positions&start_date=2025-11-27T00:00:00Z&limit=1000" > positions.json ``` --- ## 📊 Use Cases ### Dashboard de Control ```bash # Obtener datos para dashboard curl http://localhost:3000/analytics/statistics/system curl http://localhost:3000/analytics/traffic/by-route curl http://localhost:3000/analytics/delays/top-routes curl http://localhost:3000/analytics/stations/busiest?limit=5 ``` ### Análisis de Rendimiento ```bash # Análisis de una ruta específica curl http://localhost:3000/analytics/performance/route/AVE-MAD-BCN?days=30 # Estadísticas diarias últimos 30 días curl http://localhost:3000/analytics/statistics/daily?days=30 # Distancia recorrida por tren curl "http://localhost:3000/analytics/trains/12345/distance?start_time=2025-11-01T00:00:00Z" ``` ### Planificación de Viajes ```bash # Buscar estación curl "http://localhost:3000/explorer/search?query=barcelona" # Ver info completa de estación curl http://localhost:3000/explorer/stations/BARCELONA-SANTS # Planificar viaje curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS&time=08:00:00" # Ver rutas que conectan dos puntos curl "http://localhost:3000/explorer/routes/between?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS" ``` --- ## 🐛 Troubleshooting Phase 3 ### Las vistas materializadas están vacías **Causa**: No hay suficientes datos históricos o no se han refrescado. **Solución**: ```bash # Refrescar manualmente curl -X POST http://localhost:3000/analytics/refresh # Verificar en PostgreSQL make psql > SELECT COUNT(*) FROM traffic_by_hour; > SELECT COUNT(*) FROM traffic_by_route; ``` ### El planificador no encuentra viajes **Causa**: Datos GTFS Static no cargados o formato de parámetros incorrecto. **Solución**: ```bash # Verificar que hay trips y stop_times make psql > SELECT COUNT(*) FROM trips; > SELECT COUNT(*) FROM stop_times; # Verificar formato de parámetros curl "http://localhost:3000/explorer/planner?origin=STOP_ID_ORIGIN&destination=STOP_ID_DEST" ``` ### Export devuelve error **Causa**: Tabla no permitida o parámetros inválidos. **Solución**: ```bash # Ver tablas permitidas curl "http://localhost:3000/analytics/export" | jq '.allowed_tables' # Usar tabla válida curl "http://localhost:3000/analytics/export?table=routes&format=json" ``` --- ## 📝 Próximos Pasos Funcionalidades pendientes de Phase 3: - [ ] Frontend: Heatmap de tráfico en mapa - [ ] Frontend: Dashboard de estadísticas con gráficos - [ ] Frontend: UI del planificador de viajes - [ ] Frontend: Explorador de rutas interactivo - [ ] WebSocket para updates de analytics en tiempo real - [ ] Overlay de infraestructura ADIF (WMS) - [ ] Predicciones ML (Fase 4) --- ## 📚 Documentación Relacionada - [Arquitectura Completa](arquitectura-sistema-tracking-trenes.md) - [Fase 1 - MVP](FASE1-MVP.md) - [Fase 2 - Enriquecimiento](FASE2-ENRIQUECIMIENTO.md) - [Fuentes de Datos](FUENTES_DATOS.md) - [README Principal](README.md) --- **Estado**: Fase 3 - Backend Completo, Frontend Pendiente **Fecha**: 27 noviembre 2025 **Próxima Fase**: Frontend Phase 3 + Fase 4 (ML y Predicciones)