111 lines
4.4 KiB
Kotlin
111 lines
4.4 KiB
Kotlin
package com.android.trisolarisserver.component
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
import org.slf4j.LoggerFactory
|
|
import org.springframework.beans.factory.annotation.Value
|
|
import org.springframework.stereotype.Component
|
|
import org.springframework.web.client.RestTemplate
|
|
import org.springframework.web.util.UriComponentsBuilder
|
|
|
|
@Component
|
|
class GoogleGeocodingClient(
|
|
private val restTemplate: RestTemplate,
|
|
private val objectMapper: ObjectMapper,
|
|
@Value("\${google.maps.apiKey:}")
|
|
private val apiKey: String,
|
|
@Value("\${google.maps.geocode.baseUrl:https://maps.googleapis.com/maps/api/geocode/json}")
|
|
private val baseUrl: String
|
|
) {
|
|
private val logger = LoggerFactory.getLogger(GoogleGeocodingClient::class.java)
|
|
|
|
fun resolveCityState(pinCode: String): GeocodeResult {
|
|
if (apiKey.isBlank()) {
|
|
return GeocodeResult(null, null, "NO_API_KEY")
|
|
}
|
|
return try {
|
|
val url = UriComponentsBuilder.fromUriString(baseUrl)
|
|
.queryParam("components", "postal_code:$pinCode|country:IN")
|
|
.queryParam("region", "IN")
|
|
.queryParam("key", apiKey)
|
|
.toUriString()
|
|
val primary = fetch(url)
|
|
if (primary.status == "OK") {
|
|
return primary
|
|
}
|
|
if (primary.status == "ZERO_RESULTS") {
|
|
val fallbackUrl = UriComponentsBuilder.fromUriString(baseUrl)
|
|
.queryParam("address", "$pinCode India")
|
|
.queryParam("region", "IN")
|
|
.queryParam("key", apiKey)
|
|
.toUriString()
|
|
val fallback = fetch(fallbackUrl)
|
|
if (fallback.resolvedCityState != null) return fallback
|
|
return primary
|
|
}
|
|
primary
|
|
} catch (ex: Exception) {
|
|
logger.warn("Geocoding failed: {}", ex.message)
|
|
GeocodeResult(null, null, "ERROR")
|
|
}
|
|
}
|
|
|
|
private fun fetch(url: String): GeocodeResult {
|
|
val response = restTemplate.getForEntity(url, String::class.java)
|
|
val body = response.body ?: return GeocodeResult(null, null, "EMPTY_BODY")
|
|
val status = parseStatus(body)
|
|
val parsed = if (status == "OK") parseCityState(body) else null
|
|
return GeocodeResult(parsed, body, status ?: "UNKNOWN_STATUS")
|
|
}
|
|
|
|
private fun parseStatus(body: String): String? {
|
|
return objectMapper.readTree(body).path("status").asText(null)
|
|
}
|
|
|
|
private fun parseCityState(body: String): String? {
|
|
val root = objectMapper.readTree(body)
|
|
val results = root.path("results")
|
|
if (!results.isArray || results.isEmpty) return null
|
|
val resultNode = results.firstOrNull { node ->
|
|
node.path("types").any { it.asText(null) == "postal_code" }
|
|
} ?: results.first()
|
|
val components = resultNode.path("address_components")
|
|
if (!components.isArray) return null
|
|
|
|
var city: String? = null
|
|
var admin2: String? = null
|
|
var state: String? = null
|
|
for (comp in components) {
|
|
val types = comp.path("types").mapNotNull { it.asText(null) }.toSet()
|
|
when {
|
|
"locality" in types -> city = comp.path("long_name").asText(null) ?: city
|
|
"postal_town" in types -> if (city == null) {
|
|
city = comp.path("long_name").asText(null)
|
|
}
|
|
"sublocality" in types -> if (city == null) {
|
|
city = comp.path("long_name").asText(null)
|
|
}
|
|
"administrative_area_level_2" in types -> admin2 = comp.path("long_name").asText(null) ?: admin2
|
|
"administrative_area_level_1" in types -> state = comp.path("long_name").asText(null) ?: state
|
|
}
|
|
}
|
|
val preferredCity = admin2?.trim()?.ifBlank { null } ?: city?.trim()?.ifBlank { null }
|
|
if (preferredCity == null && state.isNullOrBlank()) return null
|
|
return listOfNotNull(preferredCity, state?.trim()?.ifBlank { null }).joinToString(", ")
|
|
}
|
|
}
|
|
|
|
data class GeocodeResult(
|
|
val resolvedCityState: String?,
|
|
val rawResponse: String?,
|
|
val status: String?
|
|
)
|
|
|
|
data class PincodeLookupResult(
|
|
val resolvedCityState: String?,
|
|
val rawResponse: String?,
|
|
val status: String?,
|
|
val source: String,
|
|
val errorMessage: String? = null,
|
|
val requestUrl: String? = null
|
|
)
|