# Proveedores de ubicación (integraciones extensible) ## Purpose Sistema extensible de integraciones con backends de ubicación (Google Maps, Waze, OpenStreetMap, coordenadas sueltas y geocodificación Nominatim) bajo una interfaz común (`id`/`detecta`/`extrae`), capaz de autodetectar el proveedor de una entrada libre, extraer coordenadas e inferir nombre y provincia, y ampliable a otras extracciones futuras sin modificar los proveedores existentes. ## Requirements ### Requirement: Interfaz común para proveedores de ubicación Cada integración con un backend de ubicación SHALL implementarse como una clase que cumpla una misma interfaz: identificarse (`id`), indicar si reconoce una entrada (`detecta(entrada)`) y extraer la información disponible (`extrae(entrada)`). #### Scenario: Contrato uniforme - **WHEN** se consulta cualquier proveedor registrado - **THEN** expone los métodos `detecta(entrada)` y `extrae(entrada)` con la misma firma y semántica, de modo que el consumidor no necesita conocer la clase concreta ### Requirement: Resultado de extracción extensible El método `extrae(entrada)` SHALL devolver un objeto de resultado extensible que incluya como mínimo las coordenadas (`lat`, `lng`) y el identificador del proveedor que las extrajo, pudiendo incorporar campos adicionales en el futuro sin romper a los consumidores. #### Scenario: Extracción mínima - **WHEN** un proveedor extrae una ubicación válida - **THEN** el resultado contiene `lat`, `lng` y el identificador del proveedor #### Scenario: Campos adicionales sin ruptura - **WHEN** en el futuro un proveedor añade nuevos campos al resultado (horarios, teléfono, fotos…) - **THEN** los consumidores existentes siguen funcionando sin modificación, ignorando los campos que no conocen ### Requirement: Autodetección del proveedor a partir de la entrada El sistema SHALL autodetectar automáticamente el proveedor adecuado para una entrada dada, recorriendo los proveedores registrados y usando el primero que la reconozca. #### Scenario: Enlace de Google Maps - **WHEN** el usuario pega un enlace de Google Maps (formatos `@lat,lng`, `!3d…!4d…` o `?q=lat,lng`) - **THEN** el sistema lo resuelve con el proveedor de Google Maps y obtiene las coordenadas #### Scenario: Enlace de Waze - **WHEN** el usuario pega un enlace de Waze (`waze.com/ul`, parámetro `ll=lat,lng` o búsqueda `q=`) - **THEN** el sistema lo resuelve con el proveedor de Waze y obtiene las coordenadas #### Scenario: Enlace de OpenStreetMap - **WHEN** el usuario pega un enlace de openstreetmap.org (parámetros `mlat`/`mlon` o fragmento `#map=zoom/lat/lng`) - **THEN** el sistema lo resuelve con el proveedor de OpenStreetMap y obtiene las coordenadas #### Scenario: Coordenadas geográficas sueltas - **WHEN** el usuario escribe coordenadas directamente (por ejemplo `41.3851, 2.1734`) - **THEN** el sistema lo resuelve con el proveedor de coordenadas y obtiene lat/lng #### Scenario: Texto de dirección como fallback - **WHEN** la entrada no coincide con ningún enlace soportado y parece una dirección en texto - **THEN** el sistema la resuelve con el proveedor de geocodificación (Nominatim) realizando una búsqueda por texto #### Scenario: Entrada no reconocida - **WHEN** la entrada no es reconocida por ningún proveedor ni resuelta como dirección - **THEN** el sistema informa de que no se pudo interpretar la ubicación y no avanza de pantalla ### Requirement: Inferencia de nombre y provincia Los proveedores SHALL extraer, cuando estén disponibles, los campos inferibles `nombre` y `provincia` a partir de la entrada resuelta (geocodificación inversa cuando solo hay coordenadas), normalizando la provincia al listado canónico de provincias de España. #### Scenario: Inferencia desde coordenadas - **WHEN** la entrada se resuelve a coordenadas sin más contexto - **THEN** una geocodificación inversa obtiene un nombre descriptivo y la provincia canónica (coincidencia insensible a acentos y mayúsculas con el listado de PROVINCIAS) #### Scenario: Provincia fuera del listado - **WHEN** la provincia inferida no coincide con ninguna provincia del listado canónico - **THEN** el campo provincia queda vacío para que el usuario lo seleccione manualmente ### Requirement: Extensibilidad sin modificar proveedores existentes Añadir un proveedor nuevo SHALL consistir en crear una nueva clase que implemente la interfaz y registrarla en el resolver, sin modificar las clases existentes ni la lógica de la pasarela (principio abierto/cerrado). #### Scenario: Registro de un proveedor nuevo - **WHEN** se añade una clase `MiProveedor` con `detecta`/`extrae` y se registra en la lista de proveedores - **THEN** el campo universal empieza a aceptar las entradas que ese proveedor reconoce sin ningún otro cambio de código ### Requirement: Errores de red degradados Los proveedores SHALL tratar sus errores de red o de servicio devolviendo un fallo controlado, sin romper la pasarela ni propagar excepciones al consumidor. #### Scenario: Fallo del servicio de geocodificación - **WHEN** Nominatim no responde o devuelve un error - **THEN** la pantalla de Ubicación muestra un aviso de error y el usuario puede reintentar o continuar sin inferencia