File size: 3,033 Bytes
45efbb3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import aiohttp
from typing import Optional
from dataclasses import dataclass
from Backend.core.logging import get_logger
logger = get_logger(__name__)
@dataclass
class LocationInfo:
city: Optional[str] = None
locality: Optional[str] = None
district: Optional[str] = None
state: Optional[str] = None
country: Optional[str] = None
pincode: Optional[str] = None
full_address: Optional[str] = None
class GeocodingService:
NOMINATIM_URL = "https://nominatim.openstreetmap.org/reverse"
async def reverse_geocode(self, latitude: float, longitude: float) -> LocationInfo:
params = {
"lat": latitude,
"lon": longitude,
"format": "json",
"addressdetails": 1,
"zoom": 18,
}
headers = {
"User-Agent": "CityIssueResolutionAgent/1.0"
}
try:
async with aiohttp.ClientSession() as session:
async with session.get(
self.NOMINATIM_URL,
params=params,
headers=headers,
timeout=aiohttp.ClientTimeout(total=10)
) as response:
if response.status == 200:
data = await response.json()
return self._parse_response(data)
else:
logger.warning(f"Geocoding failed: {response.status}")
return LocationInfo()
except Exception as e:
logger.error(f"Geocoding error: {e}")
return LocationInfo()
def _parse_response(self, data: dict) -> LocationInfo:
address = data.get("address", {})
city = (
address.get("city") or
address.get("town") or
address.get("municipality") or
address.get("village") or
address.get("suburb")
)
locality = (
address.get("suburb") or
address.get("neighbourhood") or
address.get("quarter") or
address.get("borough")
)
district = (
address.get("county") or
address.get("district") or
address.get("state_district")
)
state = address.get("state")
country = address.get("country")
pincode = address.get("postcode")
full_address = data.get("display_name")
return LocationInfo(
city=city,
locality=locality,
district=district,
state=state,
country=country,
pincode=pincode,
full_address=full_address,
)
async def get_city_from_coordinates(self, latitude: float, longitude: float) -> Optional[str]:
location = await self.reverse_geocode(latitude, longitude)
return location.city or location.locality or location.district
geocoding_service = GeocodingService()
|