Spaces:
Sleeping
Sleeping
| import os | |
| import uuid | |
| import tempfile | |
| import staticmaps | |
| from PIL import Image | |
| from geopy.geocoders import Nominatim | |
| from ors_client import get_ors_client, is_ors_configured | |
| TEMP_DIR = tempfile.mkdtemp(prefix="geocalc_") | |
| def generate_route_image_file(coords, start_lat, start_lon, end_lat, end_lon): | |
| """Generate route image and save to temp file.""" | |
| context = staticmaps.Context() | |
| context.set_tile_provider(staticmaps.tile_provider_OSM) | |
| # Create route line | |
| line = staticmaps.Line( | |
| [staticmaps.create_latlng(lat, lng) for lat, lng in coords], | |
| color=staticmaps.BLUE, | |
| width=4 | |
| ) | |
| context.add_object(line) | |
| # Add start marker (green) | |
| start_marker = staticmaps.Marker( | |
| staticmaps.create_latlng(start_lat, start_lon), | |
| color=staticmaps.GREEN, | |
| size=12 | |
| ) | |
| context.add_object(start_marker) | |
| # Add end marker (red) | |
| end_marker = staticmaps.Marker( | |
| staticmaps.create_latlng(end_lat, end_lon), | |
| color=staticmaps.RED, | |
| size=12 | |
| ) | |
| context.add_object(end_marker) | |
| # Generate image and save to temp file | |
| image = context.render_pillow(500, 375) | |
| # Create unique temp file path | |
| filename = f"route_{uuid.uuid4().hex[:8]}.webp" | |
| filepath = os.path.join(TEMP_DIR, filename) | |
| image.save(filepath, format='WEBP', quality=85, optimize=True) | |
| return filepath | |
| def load_image_with_title(image_path, custom_title=None): | |
| """Load image from path and optionally add custom title.""" | |
| image = Image.open(image_path) | |
| if custom_title: | |
| from PIL import ImageDraw, ImageFont | |
| draw = ImageDraw.Draw(image) | |
| # Try to use a nice font, scaled to image size | |
| font_size = max(16, image.width // 25) # Responsive font size | |
| try: | |
| font = ImageFont.truetype("Arial.ttf", font_size) | |
| except: | |
| font = ImageFont.load_default() | |
| # Add title at top center | |
| text_bbox = draw.textbbox((0, 0), custom_title, font=font) | |
| text_width = text_bbox[2] - text_bbox[0] | |
| x = (image.width - text_width) // 2 | |
| y = 5 # Reduced margin for smaller images | |
| # Add background rectangle for better readability | |
| padding = 3 # Smaller padding for smaller images | |
| draw.rectangle([x-padding, y-padding, x+text_width+padding, y+text_bbox[3]+padding], | |
| fill="white", outline="black") | |
| draw.text((x, y), custom_title, fill="black", font=font) | |
| return image | |
| def geocode_address(address): | |
| """Convert address to coordinates.""" | |
| geolocator = Nominatim(user_agent="geocalc_mcp_app_hackathon") | |
| location = geolocator.geocode(address) | |
| if location: | |
| return round(location.latitude, 4), round(location.longitude, 4) | |
| return None | |
| def calculate_route_distance_km(route_data): | |
| """Extract distance in km from route data.""" | |
| if "error" in route_data: | |
| return None | |
| meters = route_data['summary']['distance'] | |
| return round(meters / 1000, 1) | |
| def calculate_route_time_minutes(route_data): | |
| """Extract time in minutes from route data.""" | |
| if "error" in route_data: | |
| return None | |
| seconds = route_data['summary']['duration'] | |
| return int(seconds / 60) | |
| POI_CATEGORIES = { | |
| "accommodation": 108, # hotel | |
| "restaurants": 570, # restaurant | |
| "bars": 561, # bar | |
| "cafes": 564, # café | |
| "healthcare": 208, # pharmacy | |
| "shopping": 518, # supermarket | |
| "attractions": 622, # attraction | |
| "museums": 134, # museum | |
| "transport": 588, # bus_stop | |
| "banks": 192 # bank | |
| } | |
| POI_CATEGORY_LIST = list(POI_CATEGORIES.keys()) | |
| def get_poi_data(lat, lon, radius_m=1000, categories=None): | |
| """Get POI data from OpenRouteService.""" | |
| if not is_ors_configured(): | |
| return {"error": "ORS API key not configured"} | |
| client_ors = get_ors_client() | |
| # Create circular geometry around the point | |
| import math | |
| # Calculate circle geometry in lat/lon degrees | |
| earth_radius_m = 6371000 | |
| lat_rad = math.radians(lat) | |
| lon_rad = math.radians(lon) | |
| # Calculate degree offsets for the radius | |
| lat_offset = radius_m / earth_radius_m | |
| lon_offset = radius_m / (earth_radius_m * math.cos(lat_rad)) | |
| # Create a rough circle geometry | |
| circle_points = [] | |
| for angle in range(0, 360, 30): # 12 points for circle | |
| angle_rad = math.radians(angle) | |
| point_lat = lat + lat_offset * math.sin(angle_rad) | |
| point_lon = lon + lon_offset * math.cos(angle_rad) | |
| circle_points.append([point_lon, point_lat]) | |
| circle_points.append(circle_points[0]) # Close the polygon | |
| geojson_geometry = { | |
| "type": "Polygon", | |
| "coordinates": [circle_points] | |
| } | |
| params = { | |
| "request": "pois", | |
| "geojson": geojson_geometry, | |
| "sortby": "distance" | |
| } | |
| if categories: | |
| category_ids = [POI_CATEGORIES.get(cat) for cat in categories if cat in POI_CATEGORIES] | |
| if category_ids: | |
| # Limit to maximum 5 categories as per API requirement | |
| params["filter_category_ids"] = category_ids[:5] | |
| try: | |
| result = client_ors.places(**params) | |
| return result | |
| except Exception as e: | |
| return {"error": f"POI API request failed: {str(e)}"} |