|
|
"""
|
|
|
Geocoding Service - Uses Nominatim (OpenStreetMap) for free geocoding
|
|
|
"""
|
|
|
import httpx
|
|
|
from typing import Optional, Tuple
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
NOMINATIM_URL = "https://nominatim.openstreetmap.org/search"
|
|
|
USER_AGENT = "NUMIDIUM/1.0 (Intelligence System)"
|
|
|
|
|
|
|
|
|
async def geocode(location_name: str) -> Optional[Tuple[float, float]]:
|
|
|
"""
|
|
|
Convert a location name to coordinates using Nominatim.
|
|
|
Returns (latitude, longitude) or None if not found.
|
|
|
|
|
|
Note: Nominatim has rate limits (1 request/second), so be careful with batch operations.
|
|
|
"""
|
|
|
try:
|
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
|
response = await client.get(
|
|
|
NOMINATIM_URL,
|
|
|
params={
|
|
|
"q": location_name,
|
|
|
"format": "json",
|
|
|
"limit": 1,
|
|
|
"addressdetails": 0
|
|
|
},
|
|
|
headers={
|
|
|
"User-Agent": USER_AGENT
|
|
|
}
|
|
|
)
|
|
|
|
|
|
if response.status_code == 200:
|
|
|
data = response.json()
|
|
|
if data and len(data) > 0:
|
|
|
lat = float(data[0]["lat"])
|
|
|
lon = float(data[0]["lon"])
|
|
|
return (lat, lon)
|
|
|
|
|
|
return None
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"Geocoding error for '{location_name}': {e}")
|
|
|
return None
|
|
|
|
|
|
|
|
|
async def geocode_batch(location_names: list[str], delay: float = 1.0) -> dict[str, Tuple[float, float]]:
|
|
|
"""
|
|
|
Geocode multiple locations with proper rate limiting.
|
|
|
Returns a dict mapping location names to (lat, lon) tuples.
|
|
|
"""
|
|
|
results = {}
|
|
|
|
|
|
for name in location_names:
|
|
|
coords = await geocode(name)
|
|
|
if coords:
|
|
|
results[name] = coords
|
|
|
|
|
|
await asyncio.sleep(delay)
|
|
|
|
|
|
return results
|
|
|
|