mejoras en el mapa
This commit is contained in:
@ -40,65 +40,46 @@ export function useDirectionsRoute() {
|
|||||||
errorRuta.value = null;
|
errorRuta.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Importar librerías necesarias de la nueva API
|
const { DirectionsService } = await google.maps.importLibrary("routes") as any;
|
||||||
const { RoutesService } = await google.maps.importLibrary("routes") as any;
|
const directionsService = new DirectionsService();
|
||||||
const routeService = new RoutesService();
|
|
||||||
|
|
||||||
// Límite de la API de Google Maps Routes: Origen, Destino, y hasta 25 intermediates
|
const maxWaypoints = 23; // Google Directions limit is typically 25 including origin/dest
|
||||||
const maxPuntosPorChunk = 25;
|
|
||||||
const overlaps = 1;
|
|
||||||
|
|
||||||
for (let i = 0; i < paradas.length - 1; i += (maxPuntosPorChunk - overlaps)) {
|
for (let i = 0; i < paradas.length - 1; i += maxWaypoints) {
|
||||||
const chunk = paradas.slice(i, i + maxPuntosPorChunk);
|
const chunk = paradas.slice(i, i + maxWaypoints + 1);
|
||||||
if (chunk.length < 2) break;
|
if (chunk.length < 2) break;
|
||||||
|
|
||||||
const origin = {
|
const origin = { lat: chunk[0]!.latitud, lng: chunk[0]!.longitud };
|
||||||
location: {
|
const destination = { lat: chunk[chunk.length - 1]!.latitud, lng: chunk[chunk.length - 1]!.longitud };
|
||||||
latLng: {
|
|
||||||
latitude: chunk[0]!.latitud,
|
|
||||||
longitude: chunk[0]!.longitud
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const destination = {
|
const waypoints = chunk.slice(1, -1).map(p => ({
|
||||||
location: {
|
location: new google.maps.LatLng(p.latitud, p.longitud),
|
||||||
latLng: {
|
stopover: true
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await routeService.computeRoutes({
|
const result = await new Promise<google.maps.DirectionsResult>((resolve, reject) => {
|
||||||
origin,
|
directionsService.route({
|
||||||
destination,
|
origin,
|
||||||
intermediates,
|
destination,
|
||||||
travelMode: 'DRIVE' as any, // 'DRIVE' es el nuevo estandar en computeRoutes
|
waypoints,
|
||||||
routingPreference: 'TRAFFIC_UNAWARE' as any,
|
travelMode: google.maps.TravelMode.DRIVING
|
||||||
polylineQuality: 'HIGH_QUALITY' as any,
|
}, (response: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => {
|
||||||
polylineEncoding: 'ENCODED_POLYLINE' as any,
|
if (status === google.maps.DirectionsStatus.OK && response) {
|
||||||
|
resolve(response);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Directions API failed: ${status}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.routes && response.routes.length > 0) {
|
if (result.routes && result.routes.length > 0) {
|
||||||
const route = response.routes[0];
|
const route = result.routes[0];
|
||||||
if (route.polyline && route.polyline.encodedPolyline) {
|
if (route?.overview_path) {
|
||||||
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
|
||||||
|
|
||||||
const polyline = new google.maps.Polyline({
|
const polyline = new google.maps.Polyline({
|
||||||
path: path,
|
path: route.overview_path,
|
||||||
map: map,
|
map: map,
|
||||||
strokeColor: isPast ? '#9CA3AF' : '#FBBF24', // Gris para lo lejano, Amarillo para lo cercano
|
strokeColor: isPast ? '#9CA3AF' : '#FBBF24',
|
||||||
strokeWeight: isPast ? 4 : 6,
|
strokeWeight: isPast ? 4 : 6,
|
||||||
strokeOpacity: isPast ? 0.6 : 1.0,
|
strokeOpacity: isPast ? 0.6 : 1.0,
|
||||||
icons: isPast ? [{
|
icons: isPast ? [{
|
||||||
@ -107,15 +88,16 @@ export function useDirectionsRoute() {
|
|||||||
repeat: '10px'
|
repeat: '10px'
|
||||||
}] : []
|
}] : []
|
||||||
});
|
});
|
||||||
|
|
||||||
registrarPolyline(polyline);
|
registrarPolyline(polyline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} 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) {
|
} 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)}`;
|
||||||
|
|||||||
@ -424,9 +424,8 @@ export function useGoogleMaps() {
|
|||||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cargar ruta
|
const { DirectionsService } = await google.maps.importLibrary("routes") as any;
|
||||||
const { RoutesService } = await google.maps.importLibrary("routes") as any;
|
const directionsService = new DirectionsService();
|
||||||
const routeService = new RoutesService();
|
|
||||||
|
|
||||||
const tamañoChunk = 25;
|
const tamañoChunk = 25;
|
||||||
|
|
||||||
@ -434,49 +433,34 @@ export function useGoogleMaps() {
|
|||||||
const chunk = paradas.slice(i, i + tamañoChunk);
|
const chunk = paradas.slice(i, i + tamañoChunk);
|
||||||
if (chunk.length < 2) break;
|
if (chunk.length < 2) break;
|
||||||
|
|
||||||
const origin = {
|
const origin = { lat: chunk[0]!.lat, lng: chunk[0]!.lng };
|
||||||
location: {
|
const destination = { lat: chunk[chunk.length - 1]!.lat, lng: chunk[chunk.length - 1]!.lng };
|
||||||
latLng: {
|
const waypoints = chunk.slice(1, -1).map(p => ({
|
||||||
latitude: chunk[0]!.lat,
|
location: new google.maps.LatLng(p.lat, p.lng),
|
||||||
longitude: chunk[0]!.lng
|
stopover: true
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await routeService.computeRoutes({
|
const result = await new Promise<google.maps.DirectionsResult>((resolve, reject) => {
|
||||||
origin,
|
directionsService.route({
|
||||||
destination,
|
origin,
|
||||||
intermediates,
|
destination,
|
||||||
travelMode: 'DRIVE',
|
waypoints,
|
||||||
routingPreference: 'TRAFFIC_UNAWARE',
|
travelMode: google.maps.TravelMode.DRIVING
|
||||||
polylineQuality: 'HIGH_QUALITY',
|
}, (response: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => {
|
||||||
polylineEncoding: 'ENCODED_POLYLINE',
|
if (status === google.maps.DirectionsStatus.OK && response) {
|
||||||
|
resolve(response);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Directions API failed: ${status}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.routes && response.routes.length > 0) {
|
if (result.routes && result.routes.length > 0) {
|
||||||
const route = response.routes[0];
|
const route = result.routes[0];
|
||||||
if (route.polyline && route.polyline.encodedPolyline) {
|
if (route?.overview_path) {
|
||||||
const path = google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline);
|
|
||||||
|
|
||||||
const polyline = new google.maps.Polyline({
|
const polyline = new google.maps.Polyline({
|
||||||
path: path,
|
path: route.overview_path,
|
||||||
map: map.value,
|
map: map.value,
|
||||||
strokeColor: '#FBBF24',
|
strokeColor: '#FBBF24',
|
||||||
strokeWeight: 5,
|
strokeWeight: 5,
|
||||||
@ -487,11 +471,10 @@ export function useGoogleMaps() {
|
|||||||
polylinesCreadas.push(polyline);
|
polylinesCreadas.push(polyline);
|
||||||
registrarPolyline(polyline);
|
registrarPolyline(polyline);
|
||||||
|
|
||||||
// Registrar en global overlays
|
if (!globalOverlays.has(map.value!)) {
|
||||||
if (!globalOverlays.has(map.value)) {
|
globalOverlays.set(map.value!, new Set());
|
||||||
globalOverlays.set(map.value, new Set());
|
|
||||||
}
|
}
|
||||||
globalOverlays.get(map.value)!.add(polyline);
|
globalOverlays.get(map.value!)!.add(polyline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -501,7 +484,7 @@ export function useGoogleMaps() {
|
|||||||
await delay(200);
|
await delay(200);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error cargando Routes API:', e);
|
console.error('Error cargando Directions API:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return polylinesCreadas;
|
return polylinesCreadas;
|
||||||
|
|||||||
@ -56,31 +56,33 @@ export function useParadaCercana() {
|
|||||||
let mejorRutaPuntos: google.maps.LatLng[] = [];
|
let mejorRutaPuntos: google.maps.LatLng[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { RoutesService } = await google.maps.importLibrary("routes") as any;
|
const { DirectionsService } = await google.maps.importLibrary("routes") as any;
|
||||||
const routeService = new RoutesService();
|
const directionsService = new DirectionsService();
|
||||||
|
|
||||||
const routePromises = top5.map(async (stop) => {
|
const routePromises = top5.map(async (stop) => {
|
||||||
try {
|
try {
|
||||||
const response = await routeService.computeRoutes({
|
const response = await new Promise<google.maps.DirectionsResult>((resolve, reject) => {
|
||||||
origin: {
|
directionsService.route({
|
||||||
location: { latLng: { latitude: ubicacionUsuario.lat, longitude: ubicacionUsuario.lng } }
|
origin: { lat: ubicacionUsuario.lat, lng: ubicacionUsuario.lng },
|
||||||
},
|
destination: { lat: stop.latitude, lng: stop.longitude },
|
||||||
destination: {
|
travelMode: google.maps.TravelMode.WALKING
|
||||||
location: { latLng: { latitude: stop.latitude, longitude: stop.longitude } }
|
}, (result: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => {
|
||||||
},
|
if (status === google.maps.DirectionsStatus.OK && result) {
|
||||||
travelMode: 'DRIVE',
|
resolve(result);
|
||||||
routingPreference: 'TRAFFIC_UNAWARE',
|
} else {
|
||||||
polylineQuality: 'HIGH_QUALITY',
|
reject(new Error(`Directions failed: ${status}`));
|
||||||
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];
|
||||||
|
const leg = route?.legs[0];
|
||||||
return {
|
return {
|
||||||
stop,
|
stop,
|
||||||
distance: route.distanceMeters || Infinity,
|
distance: leg?.distance?.value || Infinity,
|
||||||
duration: parseInt(route.duration || "0"),
|
duration: leg?.duration?.value || 0,
|
||||||
points: route.polyline?.encodedPolyline ? google.maps.geometry.encoding.decodePath(route.polyline.encodedPolyline) : []
|
points: route?.overview_path || []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -100,7 +102,7 @@ export function useParadaCercana() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} 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
|
// 3. Fallback a la más cercana lineal si falla API
|
||||||
|
|||||||
@ -122,12 +122,12 @@ async function saveShuttle() {
|
|||||||
<div class="form-group grid-row">
|
<div class="form-group grid-row">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Nombre de la Empresa</label>
|
<label>Nombre de la Empresa</label>
|
||||||
<input v-model="shuttleForm.company_name" type="text" placeholder="Ej: Chiriqui Transfers">
|
<input v-model="shuttleForm.company_name" type="text" placeholder="Ej: Chiriqui Transfers" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Imagen del Transporte</label>
|
<label>Imagen del Transporte</label>
|
||||||
<div class="file-upload-wrapper">
|
<div class="file-upload-wrapper">
|
||||||
<input type="file" @change="handleImageChange" accept="image/*" id="file-input">
|
<input type="file" @change="handleImageChange" accept="image/*" id="file-input" />
|
||||||
<label for="file-input" class="file-label">
|
<label for="file-input" class="file-label">
|
||||||
<span class="material-icons">cloud_upload</span>
|
<span class="material-icons">cloud_upload</span>
|
||||||
{{ selectedFileName || 'SELECCIONAR IMAGEN' }}
|
{{ selectedFileName || 'SELECCIONAR IMAGEN' }}
|
||||||
@ -140,18 +140,18 @@ async function saveShuttle() {
|
|||||||
<div class="form-group grid-row">
|
<div class="form-group grid-row">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Origen</label>
|
<label>Origen</label>
|
||||||
<input v-model="shuttleForm.origin" type="text" placeholder="Boquete">
|
<input v-model="shuttleForm.origin" type="text" placeholder="Boquete" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Destino</label>
|
<label>Destino</label>
|
||||||
<input v-model="shuttleForm.destination" type="text" placeholder="Santa Catalina">
|
<input v-model="shuttleForm.destination" type="text" placeholder="Santa Catalina" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group grid-row">
|
<div class="form-group grid-row">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Tipo de Vehículo</label>
|
<label>Tipo de Vehículo</label>
|
||||||
<input v-model="shuttleForm.vehicle_type" type="text" placeholder="Mini Van Compartida">
|
<input v-model="shuttleForm.vehicle_type" type="text" placeholder="Mini Van Compartida" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Categoría</label>
|
<label>Categoría</label>
|
||||||
@ -175,22 +175,22 @@ async function saveShuttle() {
|
|||||||
<div class="form-group grid-row">
|
<div class="form-group grid-row">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Duración Estimada</label>
|
<label>Duración Estimada</label>
|
||||||
<input v-model="shuttleForm.estimated_duration" type="text" placeholder="4.5 horas">
|
<input v-model="shuttleForm.estimated_duration" type="text" placeholder="4.5 horas" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Salidas</label>
|
<label>Salidas</label>
|
||||||
<input v-model="shuttleForm.departure_times" type="text" placeholder="Todos los días 8:00 AM">
|
<input v-model="shuttleForm.departure_times" type="text" placeholder="Todos los días 8:00 AM" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group grid-row">
|
<div class="form-group grid-row">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Precio por Persona ($)</label>
|
<label>Precio por Persona ($)</label>
|
||||||
<input v-model="shuttleForm.price_per_person" type="number">
|
<input v-model="shuttleForm.price_per_person" type="number" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Precio Viaje Privado ($)</label>
|
<label>Precio Viaje Privado ($)</label>
|
||||||
<input v-model="shuttleForm.price_private_trip" type="number">
|
<input v-model="shuttleForm.price_private_trip" type="number" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -199,12 +199,12 @@ async function saveShuttle() {
|
|||||||
<label>WhatsApp (Sin +)</label>
|
<label>WhatsApp (Sin +)</label>
|
||||||
<div class="whatsapp-input">
|
<div class="whatsapp-input">
|
||||||
<span class="prefix">+</span>
|
<span class="prefix">+</span>
|
||||||
<input v-model="shuttleForm.contact_whatsapp" type="text" placeholder="50760000000">
|
<input v-model="shuttleForm.contact_whatsapp" type="text" placeholder="50760000000" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
<label>Teléfono de Llamada</label>
|
<label>Teléfono de Llamada</label>
|
||||||
<input v-model="shuttleForm.phone_number" type="text" placeholder="50760000000">
|
<input v-model="shuttleForm.phone_number" type="text" placeholder="50760000000" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ async function saveShuttle() {
|
|||||||
<span>¿Habla Inglés? (Bilingüe)</span>
|
<span>¿Habla Inglés? (Bilingüe)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="nexus-switch">
|
<div class="nexus-switch">
|
||||||
<input type="checkbox" v-model="shuttleForm.english_speaking">
|
<input type="checkbox" v-model="shuttleForm.english_speaking" />
|
||||||
<span class="slider"></span>
|
<span class="slider"></span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@ -870,4 +870,3 @@ async function saveShuttle() {
|
|||||||
.preview-panel { order: -1; }
|
.preview-panel { order: -1; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user