chore: migrate Google Maps to Routes API and clean up deprecated code
This commit is contained in:
@ -13,15 +13,15 @@ export interface Parada {
|
|||||||
export function useDirectionsRoute() {
|
export function useDirectionsRoute() {
|
||||||
const estasCargando = ref<boolean>(false);
|
const estasCargando = ref<boolean>(false);
|
||||||
const errorRuta = ref<string | null>(null);
|
const errorRuta = ref<string | null>(null);
|
||||||
const { registrarRenderer, renderers } = useMapState();
|
const { registrarPolyline, polylines } = useMapState();
|
||||||
|
|
||||||
// Limpia los tramos anteriores dibujados en el mapa
|
// Limpia los tramos anteriores dibujados en el mapa
|
||||||
const limpiarRuta = () => {
|
const limpiarRuta = () => {
|
||||||
if (renderers.value.length > 0) {
|
if (polylines.value.length > 0) {
|
||||||
renderers.value.forEach((renderer) => {
|
polylines.value.forEach((polyline) => {
|
||||||
renderer.setMap(null);
|
polyline.setMap(null);
|
||||||
});
|
});
|
||||||
renderers.value = [];
|
polylines.value = [];
|
||||||
}
|
}
|
||||||
errorRuta.value = null;
|
errorRuta.value = null;
|
||||||
};
|
};
|
||||||
@ -40,64 +40,81 @@ export function useDirectionsRoute() {
|
|||||||
errorRuta.value = null;
|
errorRuta.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const directionsService = new google.maps.DirectionsService();
|
// Importar librerías necesarias de la nueva API
|
||||||
// Límite de la API de Google Maps: Origen, Destino, y hasta 23 waypoints (25 puntos total por request)
|
const { Route } = await google.maps.importLibrary("routes") as any;
|
||||||
|
|
||||||
|
// Límite de la API de Google Maps Routes: Origen, Destino, y hasta 25 intermediates
|
||||||
const maxPuntosPorChunk = 25;
|
const maxPuntosPorChunk = 25;
|
||||||
const overlaps = 1;
|
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)) {
|
for (let i = 0; i < paradas.length - 1; i += (maxPuntosPorChunk - overlaps)) {
|
||||||
const chunk = paradas.slice(i, i + maxPuntosPorChunk);
|
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;
|
if (chunk.length < 2) break;
|
||||||
|
|
||||||
const origen = new google.maps.LatLng(chunk[0]!.latitud, chunk[0]!.longitud);
|
const origin = {
|
||||||
const destino = new google.maps.LatLng(chunk[chunk.length - 1]!.latitud, chunk[chunk.length - 1]!.longitud);
|
location: {
|
||||||
|
latLng: {
|
||||||
// Excluimos el primero y último para que sean los waypoints intermedios
|
latitude: chunk[0]!.latitud,
|
||||||
const waypoints: google.maps.DirectionsWaypoint[] = chunk.slice(1, -1).map(p => ({
|
longitude: chunk[0]!.longitud
|
||||||
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 destination = {
|
||||||
const response = await directionsService.route(request);
|
location: {
|
||||||
|
latLng: {
|
||||||
const renderer = new google.maps.DirectionsRenderer({
|
latitude: chunk[chunk.length - 1]!.latitud,
|
||||||
map: map,
|
longitude: chunk[chunk.length - 1]!.longitud
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const intermediates = chunk.slice(1, -1).map(p => ({
|
||||||
|
location: {
|
||||||
|
latLng: {
|
||||||
|
latitude: p.latitud,
|
||||||
|
longitude: p.longitud
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await Route.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,
|
||||||
});
|
});
|
||||||
|
|
||||||
renderer.setDirections(response);
|
if (response.routes && response.routes.length > 0) {
|
||||||
registrarRenderer(renderer);
|
const route = response.routes[0];
|
||||||
|
if (route.polyline && route.polyline.encodedPolyline) {
|
||||||
|
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
||||||
|
|
||||||
|
const polyline = new google.maps.Polyline({
|
||||||
|
path: path,
|
||||||
|
map: map,
|
||||||
|
strokeColor: isPast ? '#FDE68A' : '#FBBF24',
|
||||||
|
strokeWeight: isPast ? 3 : 5,
|
||||||
|
strokeOpacity: isPast ? 0.4 : 0.95,
|
||||||
|
icons: isPast ? [{
|
||||||
|
icon: { path: 'M 0,-1 0,1', strokeOpacity: 1, scale: 2 },
|
||||||
|
offset: '0',
|
||||||
|
repeat: '10px'
|
||||||
|
}] : []
|
||||||
|
});
|
||||||
|
|
||||||
|
registrarPolyline(polyline);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.warn(`SIBU | Tramo ${i} falló: `, err);
|
console.warn(`SIBU | Tramo ${i} falló con Routes API: `, 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(200);
|
||||||
await delay(300);
|
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
errorRuta.value = `Error crítico al trazar la ruta: ${err.message || String(err)}`;
|
errorRuta.value = `Error crítico al trazar la ruta: ${err.message || String(err)}`;
|
||||||
|
|||||||
@ -16,7 +16,6 @@ export function useGoogleMaps() {
|
|||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
const {
|
const {
|
||||||
registrarMarker,
|
registrarMarker,
|
||||||
registrarRenderer,
|
|
||||||
registrarPolyline,
|
registrarPolyline,
|
||||||
registrarCallbackLimpieza,
|
registrarCallbackLimpieza,
|
||||||
limpiarMapa: limpiarTodoCentralizado
|
limpiarMapa: limpiarTodoCentralizado
|
||||||
@ -58,6 +57,7 @@ export function useGoogleMaps() {
|
|||||||
await importLibrary('maps');
|
await importLibrary('maps');
|
||||||
await importLibrary('places');
|
await importLibrary('places');
|
||||||
await importLibrary('geometry');
|
await importLibrary('geometry');
|
||||||
|
await importLibrary('routes');
|
||||||
|
|
||||||
if (typeof google === 'undefined' || !google.maps) {
|
if (typeof google === 'undefined' || !google.maps) {
|
||||||
throw new Error('Google Maps se cargó pero el espacio de nombres "google.maps" no está disponible.');
|
throw new Error('Google Maps se cargó pero el espacio de nombres "google.maps" no está disponible.');
|
||||||
@ -398,76 +398,104 @@ export function useGoogleMaps() {
|
|||||||
|
|
||||||
async function addRoutePolyline(paradas: Array<{ lat: number; lng: number }>) {
|
async function addRoutePolyline(paradas: Array<{ lat: number; lng: number }>) {
|
||||||
if (!map.value) {
|
if (!map.value) {
|
||||||
console.error('Map not initialized')
|
console.error('Map not initialized');
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
if (!paradas || paradas.length < 2) {
|
if (!paradas || paradas.length < 2) {
|
||||||
console.warn("Se necesitan al menos 2 paradas para trazar una ruta.");
|
console.warn("Se necesitan al menos 2 paradas para trazar una ruta.");
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limpiar antes de dibujar una nueva ruta para evitar acumulación
|
// Limpiar antes de dibujar una nueva ruta
|
||||||
limpiarTodoCentralizado()
|
limpiarTodoCentralizado();
|
||||||
if (map.value && globalOverlays.has(map.value)) {
|
if (map.value && globalOverlays.has(map.value)) {
|
||||||
clearAllOverlaysForMap(map.value)
|
clearAllOverlaysForMap(map.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const directionsService = new google.maps.DirectionsService();
|
const polylinesCreadas: google.maps.Polyline[] = [];
|
||||||
const renderizadoresActivos: google.maps.DirectionsRenderer[] = [];
|
|
||||||
const tamañoChunk = 25;
|
|
||||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
for (let i = 0; i < paradas.length - 1; i += (tamañoChunk - 1)) {
|
try {
|
||||||
const chunk = paradas.slice(i, i + tamañoChunk);
|
// Cargar ruta
|
||||||
if (chunk.length < 2) break;
|
const { Route } = await google.maps.importLibrary("routes") as any;
|
||||||
|
|
||||||
const origen = { lat: chunk[0]!.lat, lng: chunk[0]!.lng };
|
const tamañoChunk = 25;
|
||||||
const destino = { lat: chunk[chunk.length - 1]!.lat, lng: chunk[chunk.length - 1]!.lng };
|
|
||||||
|
|
||||||
const waypoints = chunk.slice(1, -1).map(p => ({
|
for (let i = 0; i < paradas.length - 1; i += (tamañoChunk - 1)) {
|
||||||
location: { lat: p.lat, lng: p.lng },
|
const chunk = paradas.slice(i, i + tamañoChunk);
|
||||||
stopover: true
|
if (chunk.length < 2) break;
|
||||||
}));
|
|
||||||
|
|
||||||
const request = {
|
const origin = {
|
||||||
origin: origen,
|
location: {
|
||||||
destination: destino,
|
latLng: {
|
||||||
waypoints: waypoints,
|
latitude: chunk[0]!.lat,
|
||||||
travelMode: google.maps.TravelMode.DRIVING,
|
longitude: chunk[0]!.lng
|
||||||
optimizeWaypoints: false
|
}
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await directionsService.route(request);
|
|
||||||
|
|
||||||
const renderer = new google.maps.DirectionsRenderer({
|
|
||||||
map: map.value,
|
|
||||||
suppressMarkers: true,
|
|
||||||
preserveViewport: true, // Siempre conservar la vista ya que trazamos fragmentos
|
|
||||||
polylineOptions: {
|
|
||||||
strokeColor: '#FBBF24', // Amarillo consistente con paradas
|
|
||||||
strokeWeight: 5,
|
|
||||||
strokeOpacity: 0.95
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
renderer.setDirections(response);
|
try {
|
||||||
renderizadoresActivos.push(renderer);
|
const response = await Route.computeRoutes({
|
||||||
registrarRenderer(renderer); // Registrar para limpieza centralizada
|
origin,
|
||||||
|
destination,
|
||||||
|
intermediates,
|
||||||
|
travelMode: 'DRIVE',
|
||||||
|
routingPreference: 'TRAFFIC_UNAWARE',
|
||||||
|
polylineQuality: 'HIGH_QUALITY',
|
||||||
|
polylineEncoding: 'ENCODED_POLYLINE',
|
||||||
|
});
|
||||||
|
|
||||||
// Registrar en global overlays para limpiarlos después
|
if (response.routes && response.routes.length > 0) {
|
||||||
if (!globalOverlays.has(map.value)) {
|
const route = response.routes[0];
|
||||||
globalOverlays.set(map.value, new Set())
|
if (route.polyline && route.polyline.encodedPolyline) {
|
||||||
|
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
||||||
|
|
||||||
|
const polyline = new google.maps.Polyline({
|
||||||
|
path: path,
|
||||||
|
map: map.value,
|
||||||
|
strokeColor: '#FBBF24',
|
||||||
|
strokeWeight: 5,
|
||||||
|
strokeOpacity: 0.95,
|
||||||
|
geodesic: true
|
||||||
|
});
|
||||||
|
|
||||||
|
polylinesCreadas.push(polyline);
|
||||||
|
registrarPolyline(polyline);
|
||||||
|
|
||||||
|
// Registrar en global overlays
|
||||||
|
if (!globalOverlays.has(map.value)) {
|
||||||
|
globalOverlays.set(map.value, new Set());
|
||||||
|
}
|
||||||
|
globalOverlays.get(map.value)!.add(polyline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error trazando el tramo (Paradas ${i} a ${i + chunk.length}):`, error);
|
||||||
}
|
}
|
||||||
globalOverlays.get(map.value)!.add(renderer as any);
|
|
||||||
|
|
||||||
} catch (error) {
|
await delay(200);
|
||||||
console.error(`Error trazando el tramo (Paradas ${i} a ${i + chunk.length}):`, error);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
await delay(200);
|
console.error('Error cargando Routes API:', e);
|
||||||
}
|
}
|
||||||
return renderizadoresActivos;
|
|
||||||
|
return polylinesCreadas;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fitBounds(path: Array<{ lat: number; lng: number }>) {
|
function fitBounds(path: Array<{ lat: number; lng: number }>) {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { ref } from 'vue'
|
|||||||
|
|
||||||
// Registro global de todo lo que está en el mapa
|
// Registro global de todo lo que está en el mapa
|
||||||
const markers = ref<google.maps.Marker[]>([])
|
const markers = ref<google.maps.Marker[]>([])
|
||||||
const renderers = ref<google.maps.DirectionsRenderer[]>([])
|
|
||||||
const polylines = ref<google.maps.Polyline[]>([])
|
const polylines = ref<google.maps.Polyline[]>([])
|
||||||
const infoWindows = ref<google.maps.InfoWindow[]>([])
|
const infoWindows = ref<google.maps.InfoWindow[]>([])
|
||||||
const circles = ref<google.maps.Circle[]>([])
|
const circles = ref<google.maps.Circle[]>([])
|
||||||
@ -15,11 +14,7 @@ export const useMapState = () => {
|
|||||||
return marker
|
return marker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registrar un renderer
|
|
||||||
const registrarRenderer = (renderer: google.maps.DirectionsRenderer) => {
|
|
||||||
if (renderer) renderers.value.push(renderer)
|
|
||||||
return renderer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registrar una polyline
|
// Registrar una polyline
|
||||||
const registrarPolyline = (polyline: google.maps.Polyline) => {
|
const registrarPolyline = (polyline: google.maps.Polyline) => {
|
||||||
@ -48,7 +43,7 @@ export const useMapState = () => {
|
|||||||
|
|
||||||
// ⚠️ FUNCIÓN CRÍTICA: limpiar ABSOLUTAMENTE TODO del mapa
|
// ⚠️ FUNCIÓN CRÍTICA: limpiar ABSOLUTAMENTE TODO del mapa
|
||||||
const limpiarMapa = () => {
|
const limpiarMapa = () => {
|
||||||
console.log(`SIBU | Iniciando limpieza de ${markers.value.length} markers, ${renderers.value.length} renderers, ${polylines.value.length} polylines...`)
|
console.log(`SIBU | Iniciando limpieza de ${markers.value.length} markers, ${polylines.value.length} polylines...`)
|
||||||
|
|
||||||
// Eliminar markers y overlays HTML
|
// Eliminar markers y overlays HTML
|
||||||
markers.value.forEach(m => {
|
markers.value.forEach(m => {
|
||||||
@ -67,18 +62,7 @@ export const useMapState = () => {
|
|||||||
})
|
})
|
||||||
markers.value = []
|
markers.value = []
|
||||||
|
|
||||||
// Eliminar renderers de Directions
|
|
||||||
renderers.value.forEach(r => {
|
|
||||||
try {
|
|
||||||
if (r) {
|
|
||||||
if (typeof r.setMap === 'function') r.setMap(null);
|
|
||||||
if (typeof r.setDirections === 'function') r.setDirections({ routes: [] } as any);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Error limpiando renderer', e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
renderers.value = []
|
|
||||||
|
|
||||||
// Eliminar polylines
|
// Eliminar polylines
|
||||||
polylines.value.forEach(p => {
|
polylines.value.forEach(p => {
|
||||||
@ -125,12 +109,10 @@ export const useMapState = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
markers,
|
markers,
|
||||||
renderers,
|
|
||||||
polylines,
|
polylines,
|
||||||
infoWindows,
|
infoWindows,
|
||||||
circles,
|
circles,
|
||||||
registrarMarker,
|
registrarMarker,
|
||||||
registrarRenderer,
|
|
||||||
registrarPolyline,
|
registrarPolyline,
|
||||||
registrarCircle,
|
registrarCircle,
|
||||||
registrarInfoWindow,
|
registrarInfoWindow,
|
||||||
|
|||||||
@ -49,45 +49,70 @@ export function useParadaCercana() {
|
|||||||
paradasConDistLineal.sort((a, b) => a.distancia - b.distancia);
|
paradasConDistLineal.sort((a, b) => a.distancia - b.distancia);
|
||||||
const top5 = paradasConDistLineal.slice(0, 5).map(item => item.parada);
|
const top5 = paradasConDistLineal.slice(0, 5).map(item => item.parada);
|
||||||
|
|
||||||
// 2. Usar Directions API para encontrar la más cercana por calles reales
|
// 2. Usar Routes API para encontrar la más cercana por calles reales
|
||||||
let mejorParada: BusStop | null = null;
|
let mejorParada: BusStop | null = null;
|
||||||
let minimaDistanciaCalles = Infinity;
|
let minimaDistanciaCalles = Infinity;
|
||||||
let mejorDuracion = 0;
|
let mejorDuracion = 0;
|
||||||
let mejorRutaPuntos: google.maps.LatLng[] = [];
|
let mejorRutaPuntos: google.maps.LatLng[] = [];
|
||||||
|
|
||||||
const directionsService = new google.maps.DirectionsService();
|
try {
|
||||||
|
const { Route } = await google.maps.importLibrary("routes") as any;
|
||||||
|
|
||||||
for (const stop of top5) {
|
for (const stop of top5) {
|
||||||
try {
|
try {
|
||||||
const response = await directionsService.route({
|
const response = await Route.computeRoutes({
|
||||||
origin: new google.maps.LatLng(ubicacionUsuario.lat, ubicacionUsuario.lng),
|
origin: {
|
||||||
destination: new google.maps.LatLng(stop.latitude, stop.longitude),
|
location: {
|
||||||
travelMode: google.maps.TravelMode.DRIVING // Calles reales
|
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',
|
||||||
|
});
|
||||||
|
|
||||||
if (response.routes && response.routes.length > 0) {
|
if (response.routes && response.routes.length > 0) {
|
||||||
const route = response.routes[0];
|
const route = response.routes[0];
|
||||||
if (!route) continue;
|
let distTotal = 0;
|
||||||
let distTotal = 0;
|
let durTotal = 0;
|
||||||
let durTotal = 0;
|
|
||||||
|
|
||||||
if (route.legs) {
|
if (route.distanceMeters) {
|
||||||
for (const leg of route.legs) {
|
distTotal = route.distanceMeters;
|
||||||
distTotal += leg.distance?.value || 0;
|
}
|
||||||
durTotal += leg.duration?.value || 0;
|
|
||||||
|
if (route.duration) {
|
||||||
|
// La duración viene como string "123s"
|
||||||
|
durTotal = parseInt(route.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distTotal < minimaDistanciaCalles) {
|
||||||
|
minimaDistanciaCalles = distTotal;
|
||||||
|
mejorDuracion = durTotal;
|
||||||
|
mejorParada = stop;
|
||||||
|
|
||||||
|
if (route.polyline && route.polyline.encodedPolyline) {
|
||||||
|
mejorRutaPuntos = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
if (distTotal < minimaDistanciaCalles) {
|
console.warn('Error calculando ruta a parada', stop.name, e);
|
||||||
minimaDistanciaCalles = distTotal;
|
|
||||||
mejorDuracion = durTotal;
|
|
||||||
mejorParada = stop;
|
|
||||||
mejorRutaPuntos = route.overview_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.warn('Error calculando ruta a parada', stop.name, e);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error cargando Routes API en useParadaCercana', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Fallback a la más cercana lineal si falla API
|
// 3. Fallback a la más cercana lineal si falla API
|
||||||
|
|||||||
@ -642,7 +642,7 @@ function drawInternalWalkingRoute(targetStop: BusStop, originOverride?: { lat: n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateWalkingPath(origin: { lat: number, lng: number }, targetStop: BusStop) {
|
async function calculateWalkingPath(origin: { lat: number, lng: number }, targetStop: BusStop) {
|
||||||
// 1. Limpiar pulso anterior si existe
|
// 1. Limpiar pulso anterior si existe
|
||||||
if (optimalStopPulse.value) {
|
if (optimalStopPulse.value) {
|
||||||
if (typeof optimalStopPulse.value.setMap === 'function') {
|
if (typeof optimalStopPulse.value.setMap === 'function') {
|
||||||
@ -678,58 +678,71 @@ function calculateWalkingPath(origin: { lat: number, lng: number }, targetStop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Trazar línea de puntos verde siguiendo RED VIAL PRINCIPAL
|
// 2. Trazar línea de puntos verde siguiendo RED VIAL PRINCIPAL
|
||||||
const directionsService = new google.maps.DirectionsService();
|
try {
|
||||||
directionsService.route({
|
const { Route } = await google.maps.importLibrary("routes") as any;
|
||||||
origin: origin,
|
const response = await Route.computeRoutes({
|
||||||
destination: { lat: targetStop.latitude, lng: targetStop.longitude },
|
origin: {
|
||||||
travelMode: google.maps.TravelMode.DRIVING,
|
location: {
|
||||||
}, (dirResult, dirStatus) => {
|
latLng: { latitude: origin.lat, longitude: origin.lng }
|
||||||
if (dirStatus === 'OK' && dirResult && dirResult.routes && dirResult.routes[0]) {
|
}
|
||||||
const route = dirResult.routes[0];
|
},
|
||||||
const leg = route.legs?.[0];
|
destination: {
|
||||||
|
location: {
|
||||||
|
latLng: { latitude: targetStop.latitude, longitude: targetStop.longitude }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
travelMode: 'DRIVE',
|
||||||
|
routingPreference: 'TRAFFIC_UNAWARE',
|
||||||
|
polylineQuality: 'HIGH_QUALITY',
|
||||||
|
polylineEncoding: 'ENCODED_POLYLINE',
|
||||||
|
});
|
||||||
|
|
||||||
// Guardar info de navegación (ETA y Distancia) (Retirado a favor de ETA Card / Parada Cercana Banner)
|
if (response.routes && response.routes.length > 0) {
|
||||||
if (leg) {
|
const route = response.routes[0];
|
||||||
// console.log('Distancia', leg.distance?.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (walkingPolyline.value) walkingPolyline.value.setMap(null);
|
if (route.polyline && route.polyline.encodedPolyline) {
|
||||||
if (walkingPolylineBorder.value) walkingPolylineBorder.value.setMap(null);
|
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
||||||
|
|
||||||
const { registrarPolyline: regPoly } = useMapState();
|
if (walkingPolyline.value) walkingPolyline.value.setMap(null);
|
||||||
|
if (walkingPolylineBorder.value) walkingPolylineBorder.value.setMap(null);
|
||||||
|
|
||||||
// CAPA 1: Borde blanco (Para dar contraste estilo Google Maps)
|
const { registrarPolyline: regPoly } = useMapState();
|
||||||
walkingPolylineBorder.value = new google.maps.Polyline({
|
|
||||||
path: route.overview_path,
|
|
||||||
geodesic: true,
|
|
||||||
strokeColor: '#FFFFFF',
|
|
||||||
strokeOpacity: 0.9,
|
|
||||||
strokeWeight: 10, // Un poco más grueso para el borde
|
|
||||||
map: map.value,
|
|
||||||
zIndex: 5
|
|
||||||
});
|
|
||||||
regPoly(walkingPolylineBorder.value);
|
|
||||||
|
|
||||||
// CAPA 2: Línea Indigo Central (La ruta principal)
|
// CAPA 1: Borde blanco (Para dar contraste estilo Google Maps)
|
||||||
walkingPolyline.value = new google.maps.Polyline({
|
walkingPolylineBorder.value = new google.maps.Polyline({
|
||||||
path: route.overview_path,
|
path: path,
|
||||||
geodesic: true,
|
geodesic: true,
|
||||||
strokeColor: '#4285F4', // Azul Google Maps
|
strokeColor: '#FFFFFF',
|
||||||
strokeOpacity: 1.0,
|
strokeOpacity: 0.9,
|
||||||
strokeWeight: 5,
|
strokeWeight: 10,
|
||||||
map: map.value,
|
map: map.value,
|
||||||
zIndex: 10
|
zIndex: 5
|
||||||
});
|
});
|
||||||
regPoly(walkingPolyline.value);
|
regPoly(walkingPolylineBorder.value);
|
||||||
|
|
||||||
// Ajustar zoom para mostrar toda la ruta de caminata
|
// CAPA 2: Línea Indigo Central (La ruta principal)
|
||||||
if (map.value) {
|
walkingPolyline.value = new google.maps.Polyline({
|
||||||
const bounds = new google.maps.LatLngBounds();
|
path: path,
|
||||||
route.overview_path.forEach(p => bounds.extend(p));
|
geodesic: true,
|
||||||
map.value.fitBounds(bounds, { top: 100, bottom: 200, left: 50, right: 50 });
|
strokeColor: '#4285F4', // Azul Google Maps
|
||||||
|
strokeOpacity: 1.0,
|
||||||
|
strokeWeight: 5,
|
||||||
|
map: map.value,
|
||||||
|
zIndex: 10
|
||||||
|
});
|
||||||
|
regPoly(walkingPolyline.value);
|
||||||
|
|
||||||
|
// Ajustar zoom para mostrar toda la ruta de caminata
|
||||||
|
if (map.value) {
|
||||||
|
const bounds = new google.maps.LatLngBounds();
|
||||||
|
path.forEach(p => bounds.extend(p));
|
||||||
|
map.value.fitBounds(bounds, { top: 100, bottom: 200, left: 50, right: 50 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.warn('SIBU | Error trazando ruta a pie con Routes API:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user