File size: 4,154 Bytes
d69447e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""
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"