diff --git a/frontend/src/composables/useDirectionsRoute.ts b/frontend/src/composables/useDirectionsRoute.ts index 4619012..8f61791 100644 --- a/frontend/src/composables/useDirectionsRoute.ts +++ b/frontend/src/composables/useDirectionsRoute.ts @@ -40,65 +40,46 @@ export function useDirectionsRoute() { errorRuta.value = null; try { - // Importar librerías necesarias de la nueva API - const { RoutesService } = await google.maps.importLibrary("routes") as any; - const routeService = new RoutesService(); + const { DirectionsService } = await google.maps.importLibrary("routes") as any; + const directionsService = new DirectionsService(); - // Límite de la API de Google Maps Routes: Origen, Destino, y hasta 25 intermediates - const maxPuntosPorChunk = 25; - const overlaps = 1; + const maxWaypoints = 23; // Google Directions limit is typically 25 including origin/dest - for (let i = 0; i < paradas.length - 1; i += (maxPuntosPorChunk - overlaps)) { - const chunk = paradas.slice(i, i + maxPuntosPorChunk); + for (let i = 0; i < paradas.length - 1; i += maxWaypoints) { + const chunk = paradas.slice(i, i + maxWaypoints + 1); if (chunk.length < 2) break; - const origin = { - location: { - latLng: { - latitude: chunk[0]!.latitud, - longitude: chunk[0]!.longitud - } - } - }; + const origin = { lat: chunk[0]!.latitud, lng: chunk[0]!.longitud }; + const destination = { lat: chunk[chunk.length - 1]!.latitud, lng: chunk[chunk.length - 1]!.longitud }; - const destination = { - location: { - latLng: { - latitude: chunk[chunk.length - 1]!.latitud, - longitude: chunk[chunk.length - 1]!.longitud - } - } - }; - - const intermediates = chunk.slice(1, -1).map(p => ({ - location: { - latLng: { - latitude: p.latitud, - longitude: p.longitud - } - } + const waypoints = chunk.slice(1, -1).map(p => ({ + location: new google.maps.LatLng(p.latitud, p.longitud), + stopover: true })); try { - const response = await routeService.computeRoutes({ - origin, - destination, - intermediates, - travelMode: 'DRIVE' as any, // 'DRIVE' es el nuevo estandar en computeRoutes - routingPreference: 'TRAFFIC_UNAWARE' as any, - polylineQuality: 'HIGH_QUALITY' as any, - polylineEncoding: 'ENCODED_POLYLINE' as any, + const result = await new Promise((resolve, reject) => { + directionsService.route({ + origin, + destination, + waypoints, + travelMode: google.maps.TravelMode.DRIVING + }, (response: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => { + if (status === google.maps.DirectionsStatus.OK && response) { + resolve(response); + } else { + reject(new Error(`Directions API failed: ${status}`)); + } + }); }); - if (response.routes && response.routes.length > 0) { - const route = response.routes[0]; - if (route.polyline && route.polyline.encodedPolyline) { - const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline); - + if (result.routes && result.routes.length > 0) { + const route = result.routes[0]; + if (route?.overview_path) { const polyline = new google.maps.Polyline({ - path: path, + path: route.overview_path, map: map, - strokeColor: isPast ? '#9CA3AF' : '#FBBF24', // Gris para lo lejano, Amarillo para lo cercano + strokeColor: isPast ? '#9CA3AF' : '#FBBF24', strokeWeight: isPast ? 4 : 6, strokeOpacity: isPast ? 0.6 : 1.0, icons: isPast ? [{ @@ -107,15 +88,16 @@ export function useDirectionsRoute() { repeat: '10px' }] : [] }); - registrarPolyline(polyline); } } } catch (err: any) { - console.warn(`SIBU | Tramo ${i} falló con Routes API: `, err); + console.warn(`SIBU | Tramo ${i} falló con Directions API: `, err); } - await delay(200); + if (i + maxWaypoints < paradas.length - 1) { + await delay(300); + } } } catch (err: any) { errorRuta.value = `Error crítico al trazar la ruta: ${err.message || String(err)}`; diff --git a/frontend/src/composables/useGoogleMaps.ts b/frontend/src/composables/useGoogleMaps.ts index 098a679..2ac27d4 100644 --- a/frontend/src/composables/useGoogleMaps.ts +++ b/frontend/src/composables/useGoogleMaps.ts @@ -424,9 +424,8 @@ export function useGoogleMaps() { const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); try { - // Cargar ruta - const { RoutesService } = await google.maps.importLibrary("routes") as any; - const routeService = new RoutesService(); + const { DirectionsService } = await google.maps.importLibrary("routes") as any; + const directionsService = new DirectionsService(); const tamañoChunk = 25; @@ -434,49 +433,34 @@ export function useGoogleMaps() { const chunk = paradas.slice(i, i + tamañoChunk); if (chunk.length < 2) break; - const origin = { - location: { - latLng: { - latitude: chunk[0]!.lat, - longitude: chunk[0]!.lng - } - } - }; - const destination = { - location: { - latLng: { - latitude: chunk[chunk.length - 1]!.lat, - longitude: chunk[chunk.length - 1]!.lng - } - } - }; - const intermediates = chunk.slice(1, -1).map(p => ({ - location: { - latLng: { - latitude: p.lat, - longitude: p.lng - } - } + const origin = { lat: chunk[0]!.lat, lng: chunk[0]!.lng }; + const destination = { lat: chunk[chunk.length - 1]!.lat, lng: chunk[chunk.length - 1]!.lng }; + const waypoints = chunk.slice(1, -1).map(p => ({ + location: new google.maps.LatLng(p.lat, p.lng), + stopover: true })); try { - const response = await routeService.computeRoutes({ - origin, - destination, - intermediates, - travelMode: 'DRIVE', - routingPreference: 'TRAFFIC_UNAWARE', - polylineQuality: 'HIGH_QUALITY', - polylineEncoding: 'ENCODED_POLYLINE', + const result = await new Promise((resolve, reject) => { + directionsService.route({ + origin, + destination, + waypoints, + travelMode: google.maps.TravelMode.DRIVING + }, (response: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => { + if (status === google.maps.DirectionsStatus.OK && response) { + resolve(response); + } else { + reject(new Error(`Directions API failed: ${status}`)); + } + }); }); - if (response.routes && response.routes.length > 0) { - const route = response.routes[0]; - if (route.polyline && route.polyline.encodedPolyline) { - const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline); - + if (result.routes && result.routes.length > 0) { + const route = result.routes[0]; + if (route?.overview_path) { const polyline = new google.maps.Polyline({ - path: path, + path: route.overview_path, map: map.value, strokeColor: '#FBBF24', strokeWeight: 5, @@ -487,11 +471,10 @@ export function useGoogleMaps() { polylinesCreadas.push(polyline); registrarPolyline(polyline); - // Registrar en global overlays - if (!globalOverlays.has(map.value)) { - globalOverlays.set(map.value, new Set()); + if (!globalOverlays.has(map.value!)) { + globalOverlays.set(map.value!, new Set()); } - globalOverlays.get(map.value)!.add(polyline); + globalOverlays.get(map.value!)!.add(polyline); } } } catch (error) { @@ -501,7 +484,7 @@ export function useGoogleMaps() { await delay(200); } } catch (e) { - console.error('Error cargando Routes API:', e); + console.error('Error cargando Directions API:', e); } return polylinesCreadas; diff --git a/frontend/src/composables/useParadaCercana.ts b/frontend/src/composables/useParadaCercana.ts index e1636f3..e847f80 100644 --- a/frontend/src/composables/useParadaCercana.ts +++ b/frontend/src/composables/useParadaCercana.ts @@ -56,31 +56,33 @@ export function useParadaCercana() { let mejorRutaPuntos: google.maps.LatLng[] = []; try { - const { RoutesService } = await google.maps.importLibrary("routes") as any; - const routeService = new RoutesService(); + const { DirectionsService } = await google.maps.importLibrary("routes") as any; + const directionsService = new DirectionsService(); const routePromises = top5.map(async (stop) => { try { - const response = await routeService.computeRoutes({ - origin: { - location: { latLng: { latitude: ubicacionUsuario.lat, longitude: ubicacionUsuario.lng } } - }, - destination: { - location: { latLng: { latitude: stop.latitude, longitude: stop.longitude } } - }, - travelMode: 'DRIVE', - routingPreference: 'TRAFFIC_UNAWARE', - polylineQuality: 'HIGH_QUALITY', - polylineEncoding: 'ENCODED_POLYLINE', + const response = await new Promise((resolve, reject) => { + directionsService.route({ + origin: { lat: ubicacionUsuario.lat, lng: ubicacionUsuario.lng }, + destination: { lat: stop.latitude, lng: stop.longitude }, + travelMode: google.maps.TravelMode.WALKING + }, (result: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => { + if (status === google.maps.DirectionsStatus.OK && result) { + resolve(result); + } else { + reject(new Error(`Directions failed: ${status}`)); + } + }); }); if (response.routes && response.routes.length > 0) { const route = response.routes[0]; + const leg = route?.legs[0]; return { stop, - distance: route.distanceMeters || Infinity, - duration: parseInt(route.duration || "0"), - points: route.polyline?.encodedPolyline ? google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline) : [] + distance: leg?.distance?.value || Infinity, + duration: leg?.duration?.value || 0, + points: route?.overview_path || [] }; } } catch (e) { @@ -100,7 +102,7 @@ export function useParadaCercana() { } } } catch (e) { - console.error('Error cargando Routes API en useParadaCercana', e); + console.error('Error cargando Directions API en useParadaCercana', e); } // 3. Fallback a la más cercana lineal si falla API diff --git a/frontend/src/views/AdminShuttles.vue b/frontend/src/views/AdminShuttles.vue index 07b288a..1d59195 100644 --- a/frontend/src/views/AdminShuttles.vue +++ b/frontend/src/views/AdminShuttles.vue @@ -122,12 +122,12 @@ async function saveShuttle() {
- +
- +
- +
@@ -870,4 +870,3 @@ async function saveShuttle() { .preview-panel { order: -1; } } -