Spaces:
Sleeping
Sleeping
| """Core logic for fetching geotags from addresses or coordinates.""" | |
| import re | |
| from typing import List | |
| import httpx | |
| import h3 | |
| from .models import GeotagResult | |
| # Regex for validating "latitude,longitude" input | |
| COORD_REGEX = re.compile(r"^-?([1-8]?[0-9]|[1-9]0)\.{1}\d{1,15}\s*,\s*-?([1]?[0-7]?[0-9]|[1]?[1-8]?[0])\.{1}\d{1,15}$") | |
| class GeotaggingError(Exception): | |
| """Base exception for geotagging errors.""" | |
| pass | |
| class InvalidCoordinatesError(GeotaggingError): | |
| """Raised when coordinate input is invalid.""" | |
| pass | |
| class GeocodingServiceError(GeotaggingError): | |
| """Raised when the external geocoding service fails.""" | |
| pass | |
| class NoResultsFoundError(GeotaggingError): | |
| """Raised when no results are found for a query.""" | |
| pass | |
| async def fetch_geotags(q: str, resolution: int) -> List[GeotagResult]: | |
| """ | |
| Fetches geotag information for a given query string (address or lat,lng). | |
| Args: | |
| q: The address string or 'latitude,longitude' pair. | |
| resolution: The H3 resolution (0-15). | |
| Returns: | |
| A list of GeotagResult objects. | |
| Raises: | |
| InvalidCoordinatesError: If the input coordinates are malformed. | |
| GeocodingServiceError: If there's an issue connecting to the geocoding service. | |
| NoResultsFoundError: If the geocoding service returns no results. | |
| """ | |
| locations = [] | |
| if COORD_REGEX.match(q.replace(" ", "")): | |
| try: | |
| lat_str, lon_str = q.split(',') | |
| lat, lon = float(lat_str), float(lon_str) | |
| locations.append({ | |
| "lat": lat, | |
| "lon": lon, | |
| "display_name": f"Coordinates: {lat:.6f}, {lon:.6f}" | |
| }) | |
| except (ValueError, IndexError): | |
| raise InvalidCoordinatesError("Invalid coordinate format. Use 'latitude,longitude'.") | |
| else: | |
| headers = {'User-Agent': 'TrackEmDown/1.0 (nikhilsingh.io)'} | |
| url = f"https://nominatim.openstreetmap.org/search?format=json&q={q}" | |
| async with httpx.AsyncClient() as client: | |
| try: | |
| response = await client.get(url, headers=headers, timeout=10.0) | |
| response.raise_for_status() | |
| locations = response.json() | |
| except httpx.RequestError as exc: | |
| raise GeocodingServiceError(f"Error connecting to geocoding service: {exc}") | |
| if not locations: | |
| raise NoResultsFoundError("No results found for the given query.") | |
| results = [] | |
| for loc in locations: | |
| try: | |
| lat, lon = float(loc['lat']), float(loc['lon']) | |
| geotag = h3.latlng_to_cell(lat, lon, resolution) | |
| results.append( | |
| GeotagResult( | |
| address=loc.get('display_name', 'N/A'), | |
| latitude=lat, | |
| longitude=lon, | |
| geotag=geotag | |
| ) | |
| ) | |
| except (ValueError, KeyError) as e: | |
| # Skip malformed results from the external API | |
| print(f"Skipping a location due to parsing error: {e}") | |
| continue | |
| if not results: | |
| raise NoResultsFoundError("Geocoding service returned malformed data.") | |
| return results | |