Files
localesp/src/utils/geo.js
T
edgar.friendly 003b1e209f
deploy-branch / deploy (push) Failing after 6s
deploy-branch / teardown (push) Skipped
feat(propuesta): pasarela paso a paso con autodetección de ubicación y FAB
- Campo universal de ubicación (Google Maps/Waze/OSM/coordenadas/dirección)
  sobre proveedores extensibles (src/utils/ubicacion) con resolver e inferencia
  inversa Nominatim (nombre/provincia pre-rellenados, alias cooficiales)
- FAB flotante abajo a la derecha que abre la pasarela en un modal accesible
  (Escape, foco contenido, responsive); sin descripción ni puntuación;
  avisos de duplicado y filtro de palabras dentro del modal
- Aviso PWA reposicionado: tarjeta abajo a la izquierda en móvil,
  botón compacto arriba a la izquierda en PC (no tapa el FAB)
- Suite de pruebas: 88 unitarias/componente (vitest + RTL, offline) y
  10 de integración (supertest + SQLite :memory: y Nominatim real opt-in)
- CI: workflow de Gitea Actions con npm test + build en push/PR e
  integración manual
2026-08-16 01:35:02 +02:00

57 lines
2.8 KiB
JavaScript

// Utilidades de geolocalización usadas por las tarjetas y el mapa: enlaces
// para abrir un local en Google Maps o Waze, distancias y búsqueda de lugares.
// La extracción de coordenadas de enlaces y la geocodificación por texto viven
// ahora en los proveedores de src/utils/ubicacion/.
// Enlaces para abrir la ubicación de un local directamente en Google Maps o Waze,
// usando las coordenadas si existen (más preciso) o si no, el texto de la dirección.
export function enlaceGoogleMapsDesdeLocal(local) {
if (local?.lat && local?.lng) return `https://www.google.com/maps/search/?api=1&query=${local.lat},${local.lng}`;
if (local?.direccion) return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(local.direccion)}`;
return null;
}
export function enlaceWazeDesdeLocal(local) {
if (local?.lat && local?.lng) return `https://waze.com/ul?ll=${local.lat},${local.lng}&navigate=yes`;
if (local?.direccion) return `https://waze.com/ul?q=${encodeURIComponent(local.direccion)}&navigate=yes`;
return null;
}
// Distancia en kilómetros entre dos coordenadas (fórmula de Haversine),
// usada para "cerca de mí" y para mostrar la distancia en cada tarjeta.
export function distanciaKm(lat1, lng1, lat2, lng2) {
const R = 6371;
const toRad = (d) => (d * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLng = toRad(lng2 - lng1);
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
export function formatearDistancia(km) {
if (km == null) return "";
return km < 1 ? `${Math.round(km * 1000)} m` : `${km.toFixed(1)} km`;
}
// Busca lugares/negocios por nombre (autocompletar). Usa Nominatim (OpenStreetMap),
// que no requiere clave de API. Nota: no es el autocompletar "de Google" (eso exigiría
// una clave de Google Places con facturación activada) pero da el mismo resultado
// práctico: escribes un nombre y aparecen varias opciones para elegir.
export async function buscarLugares(texto) {
const q = (texto || "").trim();
if (q.length < 3) return [];
const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(`${q}, España`)}&format=json&addressdetails=1&namedetails=1&limit=6&countrycodes=es`;
const respuesta = await fetch(url, { headers: { "Accept-Language": "es", "User-Agent": "LocalesEspanoles/1.0" } });
const datos = await respuesta.json();
if (!datos?.length) return [];
return datos.map((d) => ({
id: d.place_id,
nombre: d.namedetails?.name || d.display_name.split(",")[0],
direccion: d.display_name,
provincia: d.address?.county || d.address?.province || d.address?.state || d.address?.city || "",
lat: parseFloat(d.lat),
lng: parseFloat(d.lon),
}));
}