feat: real-time bus ETA engine via distance approximation and routing
This commit is contained in:
@ -11,7 +11,10 @@ import { analyticsService } from "@/services/analyticsService";
|
||||
import { getImageUrl } from "@/utils/imageUrl";
|
||||
|
||||
import { useDirectionsRoute } from "@/composables/useDirectionsRoute";
|
||||
import { useParadaCercana } from "@/composables/useParadaCercana";
|
||||
import { useETA } from "@/composables/useETA";
|
||||
const BusStopInfoModal = defineAsyncComponent(() => import("@/components/BusStopInfoModal.vue"));
|
||||
const ETACard = defineAsyncComponent(() => import("@/components/ETACard.vue"));
|
||||
import type { BusStop } from '@/types'
|
||||
|
||||
const router = useRouter();
|
||||
@ -24,6 +27,10 @@ const couponStore = useCouponStore();
|
||||
|
||||
const { map, isLoaded, error: mapsError, initMap, addNumberedMarker, addHtmlMarker, fitBounds, setCenter, setZoom, addMarker, clearAllOverlays } = useGoogleMaps();
|
||||
const { trazarRuta, limpiarRuta, estasCargando: estasCargandoRuta, errorRuta } = useDirectionsRoute();
|
||||
const { encontrarParadaCercana, limpiarCaminata, paradaCercana, distanciaMetros, duracionCaminata } = useParadaCercana();
|
||||
const { calcularETA, busesActivos, cargando: etaCargando } = useETA();
|
||||
|
||||
const showETACard = ref(false);
|
||||
|
||||
const markers = ref<any[]>([]);
|
||||
const promoMarkers = ref<any[]>([]);
|
||||
@ -145,6 +152,8 @@ async function clearAllMapData() {
|
||||
optimalStopPulse.value = null;
|
||||
}
|
||||
limpiarRuta();
|
||||
limpiarCaminata();
|
||||
showETACard.value = false;
|
||||
|
||||
// 7. Restaurar Solo Usuario tras un breve respiro
|
||||
await nextTick();
|
||||
@ -449,6 +458,8 @@ function clearMapMarkers() {
|
||||
|
||||
// Clear directions route
|
||||
limpiarRuta();
|
||||
limpiarCaminata();
|
||||
showETACard.value = false;
|
||||
}
|
||||
|
||||
async function updateMapMarkers() {
|
||||
@ -718,45 +729,26 @@ function locateUser() {
|
||||
* Encuentra la parada más cercana dentro de la ruta seleccionada
|
||||
* y la resalta para el usuario.
|
||||
*/
|
||||
function highlightOptimalStopForRoute() {
|
||||
async function highlightOptimalStopForRoute() {
|
||||
if (!userCoords.value || routeStore.selectedRouteStops.length === 0) {
|
||||
console.warn('🤖 JARVIS: Sin ubicación o paradas para calcular parada óptima.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🤖 JARVIS: Calculando punto de abordaje óptimo sobre la ruta...');
|
||||
console.log('🤖 JARVIS: Calculando punto de abordaje óptimo sobre la ruta mediante calles...');
|
||||
|
||||
let nearestStop = null;
|
||||
let minDistance = Infinity;
|
||||
// Encontrar parada real y añadir ruta peatonal azul punteada
|
||||
await encontrarParadaCercana(userCoords.value, routeStore.selectedRouteStops as BusStop[], map.value || undefined);
|
||||
|
||||
const getDistance = (l1: any, l2: any) => {
|
||||
const R = 6371; // Radio de la Tierra en km
|
||||
const dLat = (l2.lat - l1.lat) * Math.PI / 180;
|
||||
const dLon = (l2.lng - l1.lng) * Math.PI / 180;
|
||||
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
||||
Math.cos(l1.lat * Math.PI / 180) * Math.cos(l2.lat * Math.PI / 180) *
|
||||
Math.sin(dLon/2) * Math.sin(dLon/2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
return R * c;
|
||||
};
|
||||
|
||||
routeStore.selectedRouteStops.forEach(stop => {
|
||||
const dist = getDistance(userCoords.value, { lat: stop.latitude, lng: stop.longitude });
|
||||
if (dist < minDistance) {
|
||||
minDistance = dist;
|
||||
nearestStop = stop;
|
||||
}
|
||||
});
|
||||
|
||||
if (nearestStop) {
|
||||
const stopObj = nearestStop as BusStop;
|
||||
console.log(`🤖 JARVIS: Parada óptima detectada: ${stopObj.name} (${minDistance.toFixed(2)} km)`);
|
||||
if (paradaCercana.value) {
|
||||
const stopObj = paradaCercana.value as BusStop;
|
||||
console.log(`🤖 JARVIS: Parada óptima detectada: ${stopObj.name}`);
|
||||
|
||||
// Centrar mapa en la parada para guiar al usuario
|
||||
// Centrar mapa
|
||||
setCenter(stopObj.latitude, stopObj.longitude);
|
||||
setZoom(17);
|
||||
|
||||
// Añadir el PULSO NARANJA de "Aborda aquí"
|
||||
// Añadir el PULSO NARANJA
|
||||
if (optimalStopPulse.value && typeof optimalStopPulse.value.setMap === 'function') {
|
||||
optimalStopPulse.value.setMap(null);
|
||||
}
|
||||
@ -767,12 +759,16 @@ function highlightOptimalStopForRoute() {
|
||||
{ x: -30, y: -30 }
|
||||
);
|
||||
|
||||
// Mini-notificación informativa
|
||||
// Mini-notificación (Opcional, se cubre ahora también con ETA card)
|
||||
navigationInfo.value = {
|
||||
distance: minDistance < 1 ? `${(minDistance * 1000).toFixed(0)} m` : `${minDistance.toFixed(1)} km`,
|
||||
duration: "Más cercana",
|
||||
distance: distanciaMetros.value < 1000 ? `${distanciaMetros.value.toFixed(0)} m` : `${(distanciaMetros.value/1000).toFixed(1)} km`,
|
||||
duration: "Calculada",
|
||||
targetName: stopObj.name
|
||||
};
|
||||
|
||||
// Calcular ETAs
|
||||
await calcularETA(routeStore.selectedRouteId!, stopObj);
|
||||
showETACard.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1218,6 +1214,17 @@ function clearNavigation() {
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<ETACard
|
||||
:is-open="showETACard"
|
||||
:stop-name="paradaCercana?.name || ''"
|
||||
:walk-distance="distanciaMetros"
|
||||
:walk-duration="duracionCaminata"
|
||||
:buses="busesActivos"
|
||||
:is-loading="etaCargando"
|
||||
@close="showETACard = false"
|
||||
@refresh="paradaCercana && routeStore.selectedRouteId ? calcularETA(routeStore.selectedRouteId, paradaCercana) : null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user