import { ref } from 'vue'; import { useMapState } from './useMapState'; export interface Parada { id: number; nombre: string; latitud: number; longitud: number; orden: number; } export function useDirectionsRoute() { const estasCargando = ref(false); const errorRuta = ref(null); const { registrarRenderer, renderers } = useMapState(); // Limpia los tramos anteriores dibujados en el mapa const limpiarRuta = () => { if (renderers.value.length > 0) { renderers.value.forEach((renderer) => { renderer.setMap(null); }); renderers.value = []; } errorRuta.value = null; }; // Función utilitaria para pausar ejecución const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const trazarRuta = async (paradas: Parada[], map: google.maps.Map, isPast: boolean = false) => { if (!paradas || paradas.length < 2) { errorRuta.value = 'Se requieren al menos 2 paradas para trazar una ruta.'; return; } limpiarRuta(); estasCargando.value = true; errorRuta.value = null; try { const directionsService = new google.maps.DirectionsService(); // Límite de la API de Google Maps: Origen, Destino, y hasta 23 waypoints (25 puntos total por request) const maxPuntosPorChunk = 25; const overlaps = 1; // Iteramos sobre las paradas dividiéndolas en chunks con 1 punto en común ("overlap") // para asegurar que las secciones se conecten correctamente. for (let i = 0; i < paradas.length - 1; i += (maxPuntosPorChunk - overlaps)) { const chunk = paradas.slice(i, i + maxPuntosPorChunk); // Si el chunk es muy pequeño (último fragmento o vector final), detenemos if (chunk.length < 2) break; const origen = new google.maps.LatLng(chunk[0]!.latitud, chunk[0]!.longitud); const destino = new google.maps.LatLng(chunk[chunk.length - 1]!.latitud, chunk[chunk.length - 1]!.longitud); // Excluimos el primero y último para que sean los waypoints intermedios const waypoints: google.maps.DirectionsWaypoint[] = chunk.slice(1, -1).map(p => ({ location: new google.maps.LatLng(p.latitud, p.longitud), stopover: true })); const request: google.maps.DirectionsRequest = { origin: origen, destination: destino, waypoints: waypoints, travelMode: google.maps.TravelMode.DRIVING, optimizeWaypoints: false }; try { const response = await directionsService.route(request); const renderer = new google.maps.DirectionsRenderer({ map: map, suppressMarkers: true, // SIBU maneja los suyos propios preserveViewport: true, // No auto centrar en cada tramo para evitar parpadeos visuales polylineOptions: isPast ? { strokeColor: '#FDE68A', // amarillo muy tenue strokeWeight: 3, strokeOpacity: 0.4 } : { strokeColor: '#FBBF24', // amarillo principal strokeWeight: 5, strokeOpacity: 0.95 } }); renderer.setDirections(response); registrarRenderer(renderer); } catch (err: any) { console.warn(`SIBU | Tramo ${i} falló: `, err); // La ruta continúa renderizando los siguientes tramos disponibles, no paramos todo. } // Retardo para evitar sobrecargar a la API y el error "OVER_QUERY_LIMIT" await delay(300); } } catch (err: any) { errorRuta.value = `Error crítico al trazar la ruta: ${err.message || String(err)}`; console.error(errorRuta.value); } finally { estasCargando.value = false; } }; return { trazarRuta, limpiarRuta, estasCargando, errorRuta }; }