b1016ec845
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>
103 lines
3.5 KiB
Kotlin
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)
|
|
)
|
|
}
|