Spaces:
Sleeping
Sleeping
| """ | |
| Location Configuration Module for Fleet Optimization | |
| Flexible location management for any geographic area | |
| """ | |
| import json | |
| import requests | |
| from typing import Dict, List, Tuple, Optional | |
| from dataclasses import dataclass | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class LocationConfig: | |
| """Configuration for a specific location/city""" | |
| name: str | |
| center_lat: float | |
| center_lng: float | |
| bounds: Dict[str, float] # north, south, east, west | |
| hotspots: List[Dict[str, any]] # demand hotspots with metadata | |
| timezone: str | |
| country: str | |
| city: str | |
| class LocationManager: | |
| """Manages location configurations and geographic data""" | |
| def __init__(self, google_maps_api_key: str): | |
| self.api_key = google_maps_api_key | |
| self.current_location = None | |
| self.predefined_locations = self._load_predefined_locations() | |
| def _load_predefined_locations(self) -> Dict[str, LocationConfig]: | |
| """Load predefined location configurations""" | |
| return { | |
| 'new_york': LocationConfig( | |
| name='New York City', | |
| center_lat=40.7589, | |
| center_lng=-73.9851, | |
| bounds={'north': 40.8, 'south': 40.7, 'east': -73.9, 'west': -74.0}, | |
| hotspots=[ | |
| {'name': 'Times Square', 'lat': 40.7589, 'lng': -73.9851, 'peak_hours': [18, 19, 20, 21], 'base_rate': 0.4}, | |
| {'name': 'Penn Station', 'lat': 40.7505, 'lng': -73.9934, 'peak_hours': [7, 8, 17, 18], 'base_rate': 0.5}, | |
| {'name': 'Grand Central', 'lat': 40.7527, 'lng': -73.9772, 'peak_hours': [7, 8, 17, 18], 'base_rate': 0.4}, | |
| {'name': 'Empire State', 'lat': 40.7484, 'lng': -73.9857, 'peak_hours': [10, 11, 14, 15], 'base_rate': 0.3}, | |
| {'name': 'Rockefeller', 'lat': 40.7587, 'lng': -73.9787, 'peak_hours': [12, 13, 19, 20], 'base_rate': 0.3}, | |
| {'name': 'Financial District', 'lat': 40.7282, 'lng': -74.0776, 'peak_hours': [8, 9, 17, 18], 'base_rate': 0.4}, | |
| ], | |
| timezone='America/New_York', | |
| country='USA', | |
| city='New York' | |
| ), | |
| 'london': LocationConfig( | |
| name='London', | |
| center_lat=51.5074, | |
| center_lng=-0.1278, | |
| bounds={'north': 51.6, 'south': 51.4, 'east': -0.1, 'west': -0.2}, | |
| hotspots=[ | |
| {'name': 'Trafalgar Square', 'lat': 51.5081, 'lng': -0.1281, 'peak_hours': [12, 13, 18, 19], 'base_rate': 0.4}, | |
| {'name': 'King\'s Cross', 'lat': 51.5308, 'lng': -0.1238, 'peak_hours': [7, 8, 17, 18], 'base_rate': 0.5}, | |
| {'name': 'London Bridge', 'lat': 51.5045, 'lng': -0.0865, 'peak_hours': [8, 9, 17, 18], 'base_rate': 0.4}, | |
| {'name': 'Covent Garden', 'lat': 51.5118, 'lng': -0.1233, 'peak_hours': [12, 13, 19, 20], 'base_rate': 0.3}, | |
| {'name': 'Oxford Circus', 'lat': 51.5154, 'lng': -0.1415, 'peak_hours': [11, 12, 18, 19], 'base_rate': 0.4}, | |
| {'name': 'Canary Wharf', 'lat': 51.5054, 'lng': -0.0235, 'peak_hours': [8, 9, 17, 18], 'base_rate': 0.4}, | |
| ], | |
| timezone='Europe/London', | |
| country='UK', | |
| city='London' | |
| ), | |
| 'tokyo': LocationConfig( | |
| name='Tokyo', | |
| center_lat=35.6762, | |
| center_lng=139.6503, | |
| bounds={'north': 35.8, 'south': 35.5, 'east': 139.8, 'west': 139.5}, | |
| hotspots=[ | |
| {'name': 'Shibuya Crossing', 'lat': 35.6598, 'lng': 139.7006, 'peak_hours': [18, 19, 20, 21], 'base_rate': 0.5}, | |
| {'name': 'Tokyo Station', 'lat': 35.6812, 'lng': 139.7671, 'peak_hours': [7, 8, 17, 18], 'base_rate': 0.5}, | |
| {'name': 'Ginza', 'lat': 35.6719, 'lng': 139.7650, 'peak_hours': [12, 13, 19, 20], 'base_rate': 0.4}, | |
| {'name': 'Shinjuku', 'lat': 35.6896, 'lng': 139.6917, 'peak_hours': [18, 19, 20, 21], 'base_rate': 0.4}, | |
| {'name': 'Harajuku', 'lat': 35.6702, 'lng': 139.7026, 'peak_hours': [12, 13, 18, 19], 'base_rate': 0.3}, | |
| {'name': 'Roppongi', 'lat': 35.6654, 'lng': 139.7296, 'peak_hours': [19, 20, 21, 22], 'base_rate': 0.4}, | |
| ], | |
| timezone='Asia/Tokyo', | |
| country='Japan', | |
| city='Tokyo' | |
| ), | |
| 'singapore': LocationConfig( | |
| name='Singapore', | |
| center_lat=1.3521, | |
| center_lng=103.8198, | |
| bounds={'north': 1.4, 'south': 1.3, 'east': 103.9, 'west': 103.7}, | |
| hotspots=[ | |
| {'name': 'Marina Bay', 'lat': 1.2833, 'lng': 103.8607, 'peak_hours': [12, 13, 18, 19], 'base_rate': 0.4}, | |
| {'name': 'Orchard Road', 'lat': 1.3048, 'lng': 103.8318, 'peak_hours': [12, 13, 19, 20], 'base_rate': 0.4}, | |
| {'name': 'Chinatown', 'lat': 1.2833, 'lng': 103.8444, 'peak_hours': [11, 12, 18, 19], 'base_rate': 0.3}, | |
| {'name': 'Little India', 'lat': 1.3048, 'lng': 103.8522, 'peak_hours': [11, 12, 18, 19], 'base_rate': 0.3}, | |
| {'name': 'Clarke Quay', 'lat': 1.2924, 'lng': 103.8444, 'peak_hours': [19, 20, 21, 22], 'base_rate': 0.4}, | |
| {'name': 'Sentosa', 'lat': 1.2494, 'lng': 103.8303, 'peak_hours': [10, 11, 15, 16], 'base_rate': 0.3}, | |
| ], | |
| timezone='Asia/Singapore', | |
| country='Singapore', | |
| city='Singapore' | |
| ) | |
| } | |
| def set_location(self, location_identifier: str) -> bool: | |
| """Set the current location by identifier""" | |
| if location_identifier in self.predefined_locations: | |
| self.current_location = self.predefined_locations[location_identifier] | |
| logger.info(f"Location set to: {self.current_location.name}") | |
| return True | |
| else: | |
| logger.error(f"Location '{location_identifier}' not found in predefined locations") | |
| return False | |
| def create_custom_location(self, name: str, center_lat: float, center_lng: float, | |
| bounds: Dict[str, float], hotspots: List[Dict] = None) -> LocationConfig: | |
| """Create a custom location configuration""" | |
| if hotspots is None: | |
| # Generate default hotspots around the center | |
| hotspots = self._generate_default_hotspots(center_lat, center_lng) | |
| location = LocationConfig( | |
| name=name, | |
| center_lat=center_lat, | |
| center_lng=center_lng, | |
| bounds=bounds, | |
| hotspots=hotspots, | |
| timezone='UTC', # Default timezone | |
| country='Unknown', | |
| city=name | |
| ) | |
| self.current_location = location | |
| logger.info(f"Custom location created: {name}") | |
| return location | |
| def _generate_default_hotspots(self, center_lat: float, center_lng: float, | |
| num_hotspots: int = 6) -> List[Dict]: | |
| """Generate default hotspots around a center point""" | |
| import random | |
| hotspots = [] | |
| for i in range(num_hotspots): | |
| # Generate hotspots within a reasonable radius | |
| lat_offset = random.uniform(-0.01, 0.01) | |
| lng_offset = random.uniform(-0.01, 0.01) | |
| hotspot = { | |
| 'name': f'Hotspot {i+1}', | |
| 'lat': center_lat + lat_offset, | |
| 'lng': center_lng + lng_offset, | |
| 'peak_hours': random.sample(range(24), 4), # Random peak hours | |
| 'base_rate': random.uniform(0.2, 0.5) | |
| } | |
| hotspots.append(hotspot) | |
| return hotspots | |
| def get_location_info(self) -> Dict: | |
| """Get current location information""" | |
| if not self.current_location: | |
| return {'error': 'No location set'} | |
| return { | |
| 'name': self.current_location.name, | |
| 'center': [self.current_location.center_lat, self.current_location.center_lng], | |
| 'bounds': self.current_location.bounds, | |
| 'hotspots': self.current_location.hotspots, | |
| 'timezone': self.current_location.timezone, | |
| 'country': self.current_location.country, | |
| 'city': self.current_location.city | |
| } | |
| def get_available_locations(self) -> List[str]: | |
| """Get list of available predefined locations""" | |
| return list(self.predefined_locations.keys()) | |
| def reverse_geocode(self, lat: float, lng: float) -> Optional[Dict]: | |
| """Get location information from coordinates using Google Maps API""" | |
| try: | |
| url = f"https://maps.googleapis.com/maps/api/geocode/json" | |
| params = { | |
| 'latlng': f"{lat},{lng}", | |
| 'key': self.api_key | |
| } | |
| response = requests.get(url, params=params, timeout=10) | |
| if response.status_code == 200: | |
| data = response.json() | |
| if data['results']: | |
| result = data['results'][0] | |
| return { | |
| 'formatted_address': result['formatted_address'], | |
| 'components': result['address_components'], | |
| 'place_id': result['place_id'] | |
| } | |
| return None | |
| except Exception as e: | |
| logger.error(f"Error in reverse geocoding: {e}") | |
| return None | |
| def geocode(self, address: str) -> Optional[Tuple[float, float]]: | |
| """Get coordinates from address using Google Maps API""" | |
| try: | |
| url = f"https://maps.googleapis.com/maps/api/geocode/json" | |
| params = { | |
| 'address': address, | |
| 'key': self.api_key | |
| } | |
| response = requests.get(url, params=params, timeout=10) | |
| if response.status_code == 200: | |
| data = response.json() | |
| if data['results']: | |
| location = data['results'][0]['geometry']['location'] | |
| return (location['lat'], location['lng']) | |
| return None | |
| except Exception as e: | |
| logger.error(f"Error in geocoding: {e}") | |
| return None | |
| def validate_coordinates(self, lat: float, lng: float) -> bool: | |
| """Validate if coordinates are within reasonable bounds""" | |
| return -90 <= lat <= 90 and -180 <= lng <= 180 | |
| def get_bounds_from_center(self, center_lat: float, center_lng: float, | |
| radius_km: float = 10) -> Dict[str, float]: | |
| """Calculate bounds from center point and radius""" | |
| # Approximate conversion: 1 degree ≈ 111 km | |
| lat_delta = radius_km / 111 | |
| lng_delta = radius_km / (111 * abs(center_lat / 90)) # Adjust for latitude | |
| return { | |
| 'north': center_lat + lat_delta, | |
| 'south': center_lat - lat_delta, | |
| 'east': center_lng + lng_delta, | |
| 'west': center_lng - lng_delta | |
| } | |
| def save_custom_location(self, location: LocationConfig, filename: str = None): | |
| """Save custom location to file""" | |
| if filename is None: | |
| filename = f"custom_location_{location.name.lower().replace(' ', '_')}.json" | |
| location_data = { | |
| 'name': location.name, | |
| 'center_lat': location.center_lat, | |
| 'center_lng': location.center_lng, | |
| 'bounds': location.bounds, | |
| 'hotspots': location.hotspots, | |
| 'timezone': location.timezone, | |
| 'country': location.country, | |
| 'city': location.city | |
| } | |
| with open(filename, 'w') as f: | |
| json.dump(location_data, f, indent=2) | |
| logger.info(f"Custom location saved to {filename}") | |
| def load_custom_location(self, filename: str) -> Optional[LocationConfig]: | |
| """Load custom location from file""" | |
| try: | |
| with open(filename, 'r') as f: | |
| location_data = json.load(f) | |
| location = LocationConfig( | |
| name=location_data['name'], | |
| center_lat=location_data['center_lat'], | |
| center_lng=location_data['center_lng'], | |
| bounds=location_data['bounds'], | |
| hotspots=location_data['hotspots'], | |
| timezone=location_data.get('timezone', 'UTC'), | |
| country=location_data.get('country', 'Unknown'), | |
| city=location_data.get('city', location_data['name']) | |
| ) | |
| self.current_location = location | |
| logger.info(f"Custom location loaded: {location.name}") | |
| return location | |
| except Exception as e: | |
| logger.error(f"Error loading custom location: {e}") | |
| return None | |
| # Global location manager instance | |
| location_manager = None | |
| def initialize_location_manager(google_maps_api_key: str): | |
| """Initialize the global location manager""" | |
| global location_manager | |
| location_manager = LocationManager(google_maps_api_key) | |
| return location_manager | |
| def get_location_manager(): | |
| """Get the global location manager instance""" | |
| return location_manager | |
| if __name__ == "__main__": | |
| # Test the location manager | |
| api_key = "AIzaSyBTA3eACtpCPR9DDi8EhOt1cI7Cy08Mkfg" | |
| manager = LocationManager(api_key) | |
| # Test predefined locations | |
| print("Available locations:", manager.get_available_locations()) | |
| # Test setting a location | |
| manager.set_location('new_york') | |
| print("Current location:", manager.get_location_info()) | |
| # Test custom location | |
| custom_location = manager.create_custom_location( | |
| name="San Francisco", | |
| center_lat=37.7749, | |
| center_lng=-122.4194, | |
| bounds={'north': 37.8, 'south': 37.7, 'east': -122.3, 'west': -122.5} | |
| ) | |
| print("Custom location:", custom_location.name) | |
| # Test geocoding | |
| coords = manager.geocode("Times Square, New York") | |
| if coords: | |
| print(f"Times Square coordinates: {coords}") | |
| # Test reverse geocoding | |
| location_info = manager.reverse_geocode(40.7589, -73.9851) | |
| if location_info: | |
| print(f"Location info: {location_info['formatted_address']}") | |