Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import pydeck as pdk | |
| import streamlit as st | |
| from geopy.geocoders import Nominatim | |
| # Optional imports handled inside functions or try-except blocks | |
| # Try to import google.generativeai | |
| try: | |
| import google.generativeai as genai | |
| GENAI_AVAILABLE = True | |
| except ImportError: | |
| GENAI_AVAILABLE = False | |
| def get_coordinates(destination): | |
| """ | |
| Get latitude and longitude for a destination with error handling. | |
| Args: | |
| destination (str): Name of the destination. | |
| Returns: | |
| tuple: (latitude, longitude) or (28.6139, 77.2090) if not found (Delhi default). | |
| """ | |
| try: | |
| geolocator = Nominatim(user_agent="travel_app") | |
| location = geolocator.geocode(destination) | |
| if location: | |
| return location.latitude, location.longitude | |
| else: | |
| return 28.6139, 77.2090 | |
| except Exception as e: | |
| print(f"Geocoding error: {e}") | |
| return 28.6139, 77.2090 | |
| def find_nearby_attractions(destination, search_term, radius=5000, mongodb_uri=None, gemini_api_key=None): | |
| """ | |
| Find attractions near the specified destination using MongoDB vector search. | |
| """ | |
| if not mongodb_uri or not gemini_api_key: | |
| return None | |
| if not GENAI_AVAILABLE: | |
| return None | |
| try: | |
| from pymongo import MongoClient | |
| from bson import ObjectId | |
| # Connect to MongoDB | |
| client = MongoClient(mongodb_uri) | |
| db_name = 'travel_india' | |
| collection = client[db_name]['attractions'] | |
| # Get coordinates for the destination | |
| lat, lon = get_coordinates(destination) | |
| # Create the geo query | |
| coordinates = [lon, lat] | |
| # Create a new search ID for this query | |
| search_id = ObjectId() | |
| # Set up pipeline for geospatial pre-filtering | |
| geo_pipeline = [ | |
| { | |
| "$geoNear": { | |
| "near": {"type": "Point", "coordinates": coordinates}, | |
| "distanceField": "distance", | |
| "maxDistance": radius, | |
| "spherical": True | |
| } | |
| }, | |
| { | |
| "$addFields": { | |
| "searchId": search_id | |
| } | |
| } | |
| ] | |
| # Execute the pre-filtering | |
| collection.aggregate(geo_pipeline) | |
| # Configure Gemini and generate embeddings | |
| genai.configure(api_key=gemini_api_key) | |
| # Generative embedding using Gemini | |
| embedding_result = genai.embed_content( | |
| model="models/text-embedding-004", | |
| content=search_term, | |
| task_type="retrieval_query" | |
| ) | |
| search_embedding = embedding_result['embedding'] | |
| # Vector search | |
| vector_query = { | |
| "$vectorSearch": { | |
| "index": "vector_index", | |
| "queryVector": search_embedding, | |
| "path": "embedding", | |
| "numCandidates": 10, | |
| "limit": 5, | |
| "filter": {"searchId": search_id} | |
| } | |
| } | |
| results = list(collection.aggregate([vector_query])) | |
| return { | |
| "results": results, | |
| "count": len(results), | |
| "destination": destination, | |
| "coordinates": coordinates | |
| } | |
| except Exception as e: | |
| st.warning(f"Error using MongoDB search: {str(e)}") | |
| return None | |
| def render_map(destination, lat, lon, attractions_data=None): | |
| """ | |
| Render a PyDeck map with the destination and optionally nearby attractions. | |
| Args: | |
| destination (str): Name of the destination. | |
| lat (float): Latitude. | |
| lon (float): Longitude. | |
| attractions_data (list): List of attraction dictionaries from MongoDB results. | |
| """ | |
| # Base destination data | |
| map_data = [{ | |
| 'lat': lat, | |
| 'lon': lon, | |
| 'name': destination, | |
| 'description': "Your destination", | |
| 'color': [255, 103, 31, 200], # Saffron | |
| 'radius': 1000 | |
| }] | |
| if attractions_data: | |
| for att in attractions_data: | |
| map_data.append({ | |
| 'lat': att["location"]["coordinates"][1], | |
| 'lon': att["location"]["coordinates"][0], | |
| 'name': att.get("name", "Attraction"), | |
| 'description': att.get("description", ""), | |
| 'color': [4, 106, 56, 200], # Green | |
| 'radius': 500 | |
| }) | |
| df = pd.DataFrame(map_data) | |
| view_state = pdk.ViewState(latitude=lat, longitude=lon, zoom=11, pitch=50) | |
| text_layer = pdk.Layer( | |
| 'TextLayer', | |
| data=df, | |
| get_position='[lon, lat]', | |
| get_text='name', | |
| get_size=16, | |
| get_color=[0, 0, 0, 200], | |
| get_angle=0, | |
| get_text_anchor='"middle"', | |
| get_alignment_baseline='"bottom"', | |
| ) | |
| scatter_layer = pdk.Layer( | |
| 'ScatterplotLayer', | |
| data=df, | |
| get_position='[lon, lat]', | |
| get_color='color', | |
| get_radius='radius', | |
| pickable=True, | |
| ) | |
| st.pydeck_chart(pdk.Deck( | |
| map_style=None, | |
| initial_view_state=view_state, | |
| layers=[scatter_layer, text_layer], | |
| tooltip={"text": "{name}\n{description}"} | |
| )) | |
| def extract_locations_from_itinerary(itinerary_text, gemini_api_key): | |
| """ | |
| Use Gemini to extract structured location data from the itinerary text. | |
| """ | |
| if not GENAI_AVAILABLE or not gemini_api_key: | |
| return [] | |
| try: | |
| genai.configure(api_key=gemini_api_key) | |
| model = genai.GenerativeModel('gemini-2.5-flash') | |
| prompt = f""" | |
| Extract all specific locations (cities, tourist attractions, hotels, restaurants) mentioned in this travel itinerary. | |
| Return a JSON list of objects. Each object must have: | |
| - "name": Name of the place | |
| - "latitude": Approximate latitude (float) | |
| - "longitude": Approximate longitude (float) | |
| - "day": Day number (integer, if mentioned, else 1) | |
| - "description": Brief context from text (e.g., "Day 1 activity") | |
| Itinerary: | |
| {itinerary_text[:10000]} | |
| Return ONLY valid JSON. | |
| """ | |
| response = model.generate_content(prompt) | |
| text = response.text | |
| # Clean markdown code blocks if present | |
| if "```json" in text: | |
| text = text.split("```json")[1].split("```")[0] | |
| elif "```" in text: | |
| text = text.split("```")[1].split("```")[0] | |
| import json | |
| locations = json.loads(text) | |
| return locations | |
| except Exception as e: | |
| st.warning(f"Could not extract locations: {e}") | |
| return [] | |
| def render_itinerary_map(locations): | |
| """ | |
| Render a map showing the itinerary route. | |
| """ | |
| if not locations: | |
| st.warning("No locations found to visualize.") | |
| return | |
| df = pd.DataFrame(locations) | |
| # Calculate center | |
| lat = df['latitude'].mean() | |
| lon = df['longitude'].mean() | |
| view_state = pdk.ViewState(latitude=lat, longitude=lon, zoom=10, pitch=50) | |
| # Scatter layer for points | |
| scatter_layer = pdk.Layer( | |
| 'ScatterplotLayer', | |
| data=df, | |
| get_position='[longitude, latitude]', | |
| get_color=[255, 0, 0, 200], | |
| get_radius=800, | |
| pickable=True, | |
| ) | |
| # Text layer for labels | |
| text_layer = pdk.Layer( | |
| 'TextLayer', | |
| data=df, | |
| get_position='[longitude, latitude]', | |
| get_text='name', | |
| get_size=16, | |
| get_color=[0, 0, 0, 200], | |
| get_angle=0, | |
| get_text_anchor='"middle"', | |
| get_alignment_baseline='"bottom"', | |
| ) | |
| # Path layer to connect points (sorted by day) | |
| path_data = [] | |
| if 'day' in df.columns: | |
| df_sorted = df.sort_values('day') | |
| path = df_sorted[['longitude', 'latitude']].values.tolist() | |
| path_data = [{'path': path, 'color': [0, 0, 255, 150]}] | |
| path_layer = pdk.Layer( | |
| 'PathLayer', | |
| data=path_data, | |
| pickable=True, | |
| width_scale=20, | |
| width_min_pixels=2, | |
| get_path='path', | |
| get_color='color', | |
| get_width=5 | |
| ) | |
| st.pydeck_chart(pdk.Deck( | |
| map_style=None, | |
| initial_view_state=view_state, | |
| layers=[path_layer, scatter_layer, text_layer], | |
| tooltip={"text": "Day {day}: {name}\n{description}"} | |
| )) | |