Spaces:
Sleeping
Sleeping
| import logging | |
| import requests | |
| from typing import Optional, Dict, Any, List | |
| logger = logging.getLogger(__name__) | |
| class ZipCodeData(object): | |
| """A simple data class to hold the results of our geolocation lookup.""" | |
| def __init__(self, state: str, state_abbr: str, city: str, county: str): | |
| self.state = state | |
| self.state_abbr = state_abbr | |
| self.city = city | |
| self.county = county | |
| def to_dict(self) -> Dict[str, Any]: | |
| return { | |
| "state": self.state, | |
| "state_abbreviation": self.state_abbr, | |
| "city": self.city, | |
| "county": self.county | |
| } | |
| def get_lat_lon_from_zip(zip_code: str) -> Optional[Dict[str, float]]: | |
| """ | |
| Step 1: Get latitude and longitude from a ZIP code using a simple API. | |
| We'll use zippopotam.us for this first step. | |
| """ | |
| url = f"https://api.zippopotam.us/us/{zip_code}" | |
| logger.info(f"Fetching lat/lon for ZIP code: {zip_code} from {url}") | |
| try: | |
| response = requests.get(url, timeout=10) | |
| response.raise_for_status() | |
| data = response.json() | |
| if not data.get("places"): | |
| logger.warning(f"No places found for ZIP code {zip_code}") | |
| return None | |
| place = data["places"][0] | |
| return { | |
| "latitude": float(place["latitude"]), | |
| "longitude": float(place["longitude"]), | |
| "state": place["state"], | |
| "state_abbr": place["state abbreviation"], | |
| "city": place["place name"] | |
| } | |
| except (requests.RequestException, KeyError, ValueError) as e: | |
| logger.error(f"Failed to get lat/lon for ZIP {zip_code}: {e}") | |
| return None | |
| def get_county_from_lat_lon(lat: float, lon: float) -> Optional[str]: | |
| """ | |
| Step 2: Get county information from latitude and longitude using the | |
| U.S. Census Bureau's Geocoding API. | |
| """ | |
| url = "https://geocoding.geo.census.gov/geocoder/geographies/coordinates" | |
| params = { | |
| 'x': lon, | |
| 'y': lat, | |
| 'benchmark': 'Public_AR_Current', | |
| 'vintage': 'Current_Current', | |
| 'format': 'json' | |
| } | |
| logger.info(f"Fetching county for coordinates: (lat={lat}, lon={lon}) from Census Bureau API") | |
| try: | |
| response = requests.get(url, params=params, timeout=15) | |
| response.raise_for_status() | |
| data = response.json() | |
| geographies = data.get("result", {}).get("geographies", {}) | |
| counties = geographies.get("Counties", []) | |
| if counties: | |
| county_name = counties[0].get("NAME") | |
| logger.info(f"Found county: {county_name}") | |
| return county_name | |
| else: | |
| logger.warning(f"No county found for coordinates (lat={lat}, lon={lon})") | |
| return None | |
| except (requests.RequestException, KeyError, ValueError) as e: | |
| logger.error(f"Failed to get county from coordinates: {e}") | |
| return None | |
| def get_geo_data_from_zip(zip_code: str) -> Optional[ZipCodeData]: | |
| """ | |
| Orchestrates the two-step process to get state, city, and county from a ZIP code. | |
| """ | |
| # Step 1: Get Lat/Lon and basic info | |
| geo_basics = get_lat_lon_from_zip(zip_code) | |
| if not geo_basics: | |
| return None | |
| # Step 2: Get County from Lat/Lon | |
| county = get_county_from_lat_lon(geo_basics["latitude"], geo_basics["longitude"]) | |
| if not county: | |
| # Fallback: sometimes county info is not available, but we can proceed without it | |
| logger.warning(f"Could not determine county for ZIP {zip_code}, proceeding without it.") | |
| county = "Unknown" | |
| return ZipCodeData( | |
| state=geo_basics["state"], | |
| state_abbr=geo_basics["state_abbr"], | |
| city=geo_basics["city"], | |
| county=county | |
| ) |