mashrur950's picture
Initial commit: FleetMind MCP with GitHub Actions auto-sync
d69447e
raw
history blame
4.15 kB
"""
Geocoding service for FleetMind
Handles address validation with HERE API and smart mock fallback
"""
import os
import requests
import logging
from typing import Dict, Optional
logger = logging.getLogger(__name__)
# Common city coordinates for mock geocoding
CITY_COORDINATES = {
"san francisco": (37.7749, -122.4194),
"sf": (37.7749, -122.4194),
"new york": (40.7128, -74.0060),
"nyc": (40.7128, -74.0060),
"los angeles": (34.0522, -118.2437),
"la": (34.0522, -118.2437),
"chicago": (41.8781, -87.6298),
"houston": (29.7604, -95.3698),
"phoenix": (33.4484, -112.0740),
"philadelphia": (39.9526, -75.1652),
"san antonio": (29.4241, -98.4936),
"san diego": (32.7157, -117.1611),
"dallas": (32.7767, -96.7970),
"austin": (30.2672, -97.7431),
"seattle": (47.6062, -122.3321),
"boston": (42.3601, -71.0589),
"denver": (39.7392, -104.9903),
"miami": (25.7617, -80.1918),
"atlanta": (33.7490, -84.3880),
"portland": (45.5152, -122.6784),
}
class GeocodingService:
"""Handle address geocoding with HERE API and smart mock fallback"""
def __init__(self):
self.here_api_key = os.getenv("HERE_API_KEY", "")
self.use_mock = not self.here_api_key or self.here_api_key.startswith("your_")
if self.use_mock:
logger.info("Geocoding: Using mock (HERE_API_KEY not configured)")
else:
logger.info("Geocoding: Using HERE Maps API")
def geocode(self, address: str) -> Dict:
"""
Geocode address, using mock if API unavailable
Args:
address: Street address to geocode
Returns:
Dict with keys: lat, lng, formatted_address, confidence
"""
if self.use_mock:
return self._geocode_mock(address)
else:
try:
return self._geocode_here(address)
except Exception as e:
logger.error(f"HERE API failed: {e}, falling back to mock")
return self._geocode_mock(address)
def _geocode_here(self, address: str) -> Dict:
"""Real HERE API geocoding"""
url = "https://geocode.search.hereapi.com/v1/geocode"
params = {
"q": address,
"apiKey": self.here_api_key
}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if not data.get("items"):
# No results found, fall back to mock
logger.warning(f"HERE API found no results for: {address}")
return self._geocode_mock(address)
# Get first result
item = data["items"][0]
position = item["position"]
return {
"lat": position["lat"],
"lng": position["lng"],
"formatted_address": item.get("address", {}).get("label", address),
"confidence": "high (HERE API)"
}
def _geocode_mock(self, address: str) -> Dict:
"""
Smart mock geocoding for testing
Tries to detect city name and use approximate coordinates
"""
address_lower = address.lower()
# Try to find a city match
for city, coords in CITY_COORDINATES.items():
if city in address_lower:
logger.info(f"Mock geocoding detected city: {city}")
return {
"lat": coords[0],
"lng": coords[1],
"formatted_address": address,
"confidence": f"medium (mock - {city})"
}
# Default to San Francisco if no city detected
logger.info("Mock geocoding: Using default SF coordinates")
return {
"lat": 37.7749,
"lng": -122.4194,
"formatted_address": address,
"confidence": "low (mock - default)"
}
def get_status(self) -> str:
"""Get geocoding service status"""
if self.use_mock:
return "⚠️ Using mock geocoding (add HERE_API_KEY for real)"
else:
return "✅ HERE Maps API connected"