Files
fn_registry/kotlin/functions/infra/nominatim_reverse_geocode.kt
T
egutierrez b1016ec845 chore: add Kotlin directory structure, update registry.db and gitignore
Añade estructura inicial kotlin/functions/, actualiza registry.db con todos
los cambios indexados, y ajusta .gitignore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:47:27 +02:00

103 lines
3.5 KiB
Kotlin

package infra
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL
data class GeocodedLocation(
val displayName: String,
val street: String,
val houseNumber: String,
val neighbourhood: String,
val city: String,
val state: String,
val country: String,
val postcode: String,
val lat: Double,
val lon: Double,
val osmType: String,
val osmId: Long
)
/**
* nominatimReverseGeocode — Convierte coordenadas lat/lon a una dirección estructurada
* usando la API pública de Nominatim (OpenStreetMap).
*
* @param lat Latitud en grados decimales.
* @param lon Longitud en grados decimales.
* @param lang Código de idioma ISO 639-1 para la respuesta. Por defecto "es".
* @return GeocodedLocation con los campos normalizados de la dirección.
* @throws RuntimeException si la petición HTTP falla o el servidor retorna error.
*/
fun nominatimReverseGeocode(lat: Double, lon: Double, lang: String = "es"): GeocodedLocation {
val url = URL(
"https://nominatim.openstreetmap.org/reverse" +
"?format=jsonv2" +
"&lat=$lat" +
"&lon=$lon" +
"&accept-language=$lang" +
"&zoom=18"
)
val conn = url.openConnection() as HttpURLConnection
conn.setRequestProperty("User-Agent", "fn_registry/1.0")
conn.connectTimeout = 5_000
conn.readTimeout = 5_000
conn.requestMethod = "GET"
val responseCode = conn.responseCode
if (responseCode != HttpURLConnection.HTTP_OK) {
conn.disconnect()
throw RuntimeException(
"nominatimReverseGeocode: HTTP $responseCode para lat=$lat, lon=$lon"
)
}
val body = try {
conn.inputStream.bufferedReader(Charsets.UTF_8).use { it.readText() }
} catch (e: Exception) {
conn.disconnect()
throw RuntimeException(
"nominatimReverseGeocode: error leyendo respuesta — ${e.message}", e
)
} finally {
conn.disconnect()
}
val data = JSONObject(body)
val address = data.optJSONObject("address") ?: JSONObject()
val city = when {
address.has("city") && address.getString("city").isNotEmpty() ->
address.getString("city")
address.has("town") && address.getString("town").isNotEmpty() ->
address.getString("town")
address.has("village") && address.getString("village").isNotEmpty() ->
address.getString("village")
else -> ""
}
val neighbourhood = when {
address.has("neighbourhood") && address.getString("neighbourhood").isNotEmpty() ->
address.getString("neighbourhood")
address.has("suburb") && address.getString("suburb").isNotEmpty() ->
address.getString("suburb")
else -> ""
}
return GeocodedLocation(
displayName = data.optString("display_name", ""),
street = address.optString("road", ""),
houseNumber = address.optString("house_number", ""),
neighbourhood = neighbourhood,
city = city,
state = address.optString("state", ""),
country = address.optString("country", ""),
postcode = address.optString("postcode", ""),
lat = data.optString("lat", lat.toString()).toDoubleOrNull() ?: lat,
lon = data.optString("lon", lon.toString()).toDoubleOrNull() ?: lon,
osmType = data.optString("osm_type", ""),
osmId = data.optLong("osm_id", 0L)
)
}