perf: optimization for maps & network

This commit is contained in:
2026-02-26 12:39:15 -05:00
parent ba7631dc9c
commit 7b3141e5e9
5 changed files with 299 additions and 28 deletions

View File

@ -0,0 +1,110 @@
import { ref } from 'vue';
export interface Parada {
id: number;
nombre: string;
latitud: number;
longitud: number;
orden: number;
}
export function useDirectionsRoute() {
const estasCargando = ref<boolean>(false);
const errorRuta = ref<string | null>(null);
const renderizadoresActivos = ref<google.maps.DirectionsRenderer[]>([]);
// Limpia los tramos anteriores dibujados en el mapa
const limpiarRuta = () => {
if (renderizadoresActivos.value.length > 0) {
renderizadoresActivos.value.forEach((renderer) => {
renderer.setMap(null);
});
renderizadoresActivos.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) => {
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: {
strokeColor: '#1E40AF', // Azul (Tailwind blue-800)
strokeWeight: 5,
strokeOpacity: 0.8
}
});
renderer.setDirections(response);
renderizadoresActivos.value.push(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
};
}