mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2026-07-30 23:38:02 -04:00
108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
from dataclasses import dataclass
|
|
from typing import Any, Dict, Optional
|
|
|
|
from django.conf import settings
|
|
|
|
from adventures.providers.geocoding.google import reverse_geocode as google_reverse
|
|
from adventures.providers.geocoding.osm import reverse_geocode as osm_reverse
|
|
from adventures.utils.geocoding_utils import (
|
|
_extract_google_location_name,
|
|
_extract_osm_location_name,
|
|
_fetch_google_nearby_place_name,
|
|
_parse_google_address_components,
|
|
extractIsoCode,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class ReverseGeocodeResult:
|
|
data: Dict[str, Any]
|
|
provider_used: Optional[str]
|
|
|
|
|
|
def _normalize_google_geocode_payload(payload: Dict[str, Any], lat: float, lon: float, api_key: str) -> Dict[str, Any]:
|
|
results = (payload or {}).get("results", []) or []
|
|
if not results:
|
|
return {"error": "No location found for the given coordinates."}
|
|
|
|
nearby_place_name = _fetch_google_nearby_place_name(lat, lon, api_key)
|
|
location_name = _extract_google_location_name(results, nearby_place_name=nearby_place_name)
|
|
|
|
first_result = results[0]
|
|
address_result = next(
|
|
(result for result in results if "plus_code" not in result.get("types", [])),
|
|
first_result,
|
|
)
|
|
|
|
return {
|
|
"name": first_result.get("formatted_address"),
|
|
"location_name": location_name,
|
|
"address": _parse_google_address_components(address_result.get("address_components", [])),
|
|
}
|
|
|
|
|
|
def _reverse_geocode_quality(result: Dict[str, Any]) -> int:
|
|
if not result or result.get("error"):
|
|
return 0
|
|
|
|
score = 0
|
|
if result.get("region_id"):
|
|
score += 3
|
|
if result.get("city_id"):
|
|
score += 2
|
|
if result.get("country_id"):
|
|
score += 1
|
|
if result.get("location_name"):
|
|
score += 1
|
|
if result.get("display_name"):
|
|
score += 1
|
|
return score
|
|
|
|
|
|
def reverse_geocode(lat: float, lon: float, user) -> ReverseGeocodeResult:
|
|
google_result = None
|
|
|
|
api_key = getattr(settings, "GOOGLE_MAPS_API_KEY", None)
|
|
if api_key:
|
|
google_payload = google_reverse(lat, lon, api_key)
|
|
if not google_payload.error:
|
|
normalized_google = _normalize_google_geocode_payload(
|
|
google_payload.data, lat=lat, lon=lon, api_key=api_key
|
|
)
|
|
if normalized_google.get("error"):
|
|
google_result = normalized_google
|
|
else:
|
|
google_result = extractIsoCode(user, normalized_google)
|
|
if isinstance(google_result, dict) and "error" not in google_result:
|
|
google_result["provider_used"] = "google"
|
|
google_result["provider"] = "google"
|
|
|
|
if google_result and not google_result.get("error"):
|
|
if _reverse_geocode_quality(google_result) >= 5:
|
|
return ReverseGeocodeResult(data=google_result, provider_used="google")
|
|
|
|
osm_payload = osm_reverse(lat, lon)
|
|
osm_result = None
|
|
if not osm_payload.error:
|
|
osm_data = osm_payload.data
|
|
osm_data["location_name"] = _extract_osm_location_name(osm_data)
|
|
osm_result = extractIsoCode(user, osm_data)
|
|
if isinstance(osm_result, dict) and "error" not in osm_result:
|
|
osm_result["provider_used"] = "osm"
|
|
osm_result["provider"] = "osm"
|
|
|
|
if osm_result and not osm_result.get("error"):
|
|
if google_result and not google_result.get("error"):
|
|
google_score = _reverse_geocode_quality(google_result)
|
|
osm_score = _reverse_geocode_quality(osm_result)
|
|
best = google_result if google_score >= osm_score else osm_result
|
|
used = "google" if best is google_result else "osm"
|
|
return ReverseGeocodeResult(data=best, provider_used=used)
|
|
|
|
return ReverseGeocodeResult(data=osm_result, provider_used="osm")
|
|
|
|
if google_result:
|
|
return ReverseGeocodeResult(data=google_result, provider_used="google")
|
|
|
|
return ReverseGeocodeResult(data=osm_result or {"error": "Unable to reverse geocode location."}, provider_used=None)
|