---
name: overpass_nearby_pois
kind: function
lang: kt
domain: infra
version: "1.0.0"
purity: impure
signature: "fun overpassNearbyPois(lat: Double, lon: Double, radiusM: Int = 500, categories: List<String>? = null): List<POI>"
description: "Consulta la Overpass API (OpenStreetMap) para obtener POIs cercanos a una coordenada. Soporta 16 categorias mapeadas a tags OSM (amenity, tourism, historic, leisure, shop). Sin dependencias externas: solo Android SDK (HttpURLConnection + org.json)."
tags: [overpass, openstreetmap, osm, poi, geospatial, location, android, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports:
  - "org.json.JSONObject"
  - "org.json.JSONArray"
  - "java.net.HttpURLConnection"
  - "java.net.URL"
  - "java.net.URLEncoder"
tested: false
tests: []
test_file_path: ""
file_path: "kotlin/functions/infra/overpass_nearby_pois.kt"
params:
  - name: lat
    desc: "Latitud del punto central en WGS84 (ej: 40.4168 para Madrid)"
  - name: lon
    desc: "Longitud del punto central en WGS84 (ej: -3.7038 para Madrid)"
  - name: radiusM
    desc: "Radio de busqueda en metros. Default 500. Valores grandes aumentan el tiempo de respuesta."
  - name: categories
    desc: "Lista de categorias a filtrar: restaurant, cafe, bar, museum, monument, park, library, theatre, cinema, gallery, historic, tourism, shop, hotel, pharmacy, hospital. Null devuelve todas."
output: "Lista de POI con id OSM, coordenadas (lat/lon), nombre, categoria y mapa completo de tags OSM. Lanza RuntimeException si la API devuelve error HTTP o la respuesta no es JSON valido."
---

## Ejemplo

```kotlin
// Restaurantes y museos en un radio de 300 metros
val pois = overpassNearbyPois(
    lat = 40.4168,
    lon = -3.7038,
    radiusM = 300,
    categories = listOf("restaurant", "museum")
)
pois.forEach { poi ->
    println("${poi.name} (${poi.category}) — lat=${poi.lat}, lon=${poi.lon}")
}

// Todos los POIs en 500 metros (null = todas las categorias)
val all = overpassNearbyPois(lat = 48.8566, lon = 2.3522)
println("Paris: ${all.size} POIs encontrados")
```

## Categorias soportadas

| Categoria    | Tag OSM              |
|--------------|----------------------|
| restaurant   | amenity=restaurant   |
| cafe         | amenity=cafe         |
| bar          | amenity=bar          |
| museum       | tourism=museum       |
| monument     | historic=monument    |
| park         | leisure=park         |
| library      | amenity=library      |
| theatre      | amenity=theatre      |
| cinema       | amenity=cinema       |
| gallery      | tourism=gallery      |
| historic     | historic (wildcard)  |
| tourism      | tourism (wildcard)   |
| shop         | shop (wildcard)      |
| hotel        | tourism=hotel        |
| pharmacy     | amenity=pharmacy     |
| hospital     | amenity=hospital     |

## Query Overpass QL generada

```
[timeout:10][out:json];
(
  node["amenity"="restaurant"](around:500,40.4168,-3.7038);
  node["tourism"="museum"](around:500,40.4168,-3.7038);
);
out body;
```

## Notas

- Usa `java.net.HttpURLConnection` con POST y `data=<query_url_encoded>`.
- Timeouts: 10 segundos de conexion y lectura. La query QL tambien declara `[timeout:10]`.
- El tipo `POI` se define en el mismo archivo como `data class`.
- El mapa `tags` contiene todos los tags OSM del nodo, no solo el de la categoria.
- Si un nodo no tiene tag `name`, el fallback es la categoria y luego "Unknown".
- Las categorias wildcard (historic, tourism, shop) usan `node["key"](around:...)` sin valor.
- Lanza `RuntimeException` en caso de categorias invalidas, error HTTP, o JSON malformado.
- Compatible con Android SDK >= 19 (API level 19 tiene org.json integrado).
---
