|
|
from __future__ import annotations |
|
|
from typing import Optional, Dict |
|
|
import os |
|
|
import requests |
|
|
from functools import lru_cache |
|
|
|
|
|
|
|
|
def _ua() -> Dict[str, str]: |
|
|
email = os.getenv("GEOCODING_EMAIL") or os.getenv("CONTACT_EMAIL") or "example@example.com" |
|
|
return {"User-Agent": f"HF-Space-Trip-Planner/1.0 (contact: {email})"} |
|
|
|
|
|
@lru_cache(maxsize=512) |
|
|
def geocode_city(city: str) -> Optional[Dict[str, object]]: |
|
|
""" |
|
|
文字列の都市名から緯度経度を取得(Nominatim / OpenStreetMap)。 |
|
|
成功時: {"lat": float, "lon": float, "display_name": str} |
|
|
失敗時: None |
|
|
""" |
|
|
if not city or not str(city).strip(): |
|
|
return None |
|
|
|
|
|
q = str(city).strip() |
|
|
url = "https://nominatim.openstreetmap.org/search" |
|
|
params = { |
|
|
"q": q, |
|
|
"format": "jsonv2", |
|
|
"addressdetails": 1, |
|
|
"limit": 1, |
|
|
} |
|
|
try: |
|
|
r = requests.get(url, params=params, headers=_ua(), timeout=20) |
|
|
r.raise_for_status() |
|
|
items = r.json() |
|
|
if not items: |
|
|
return None |
|
|
j = items[0] |
|
|
lat = float(j["lat"]) |
|
|
lon = float(j["lon"]) |
|
|
name = j.get("display_name") or q |
|
|
return {"lat": lat, "lon": lon, "display_name": name} |
|
|
except Exception: |
|
|
return None |
|
|
|