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)}"}