swayam-the-coder commited on
Commit
bb8d628
·
verified ·
1 Parent(s): ece23f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -135
app.py CHANGED
@@ -1,22 +1,18 @@
1
  import streamlit as st
2
  import google.generativeai as genai
3
- import googlemaps
4
  from datetime import datetime
5
  from PIL import Image as PILImage
6
  import folium
7
  from streamlit_folium import folium_static
8
  import os
9
- from dotenv import load_dotenv
10
  from streamlit_option_menu import option_menu
11
  import plotly.graph_objs as go
 
12
  import re
13
 
14
- # Load environment variables
15
- load_dotenv()
16
-
17
  # Initialize APIs
18
- MAP_API_KEY = os.getenv('MAP_API_KEY')
19
- GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
20
 
21
  # Configure the page
22
  st.set_page_config(
@@ -26,259 +22,294 @@ st.set_page_config(
26
  initial_sidebar_state="expanded"
27
  )
28
 
29
- # Custom CSS for typography, headings, and styling
 
 
 
 
 
30
  st.markdown(
31
- """
32
  <style>
33
- body {
 
 
34
  background-color: #f5f7fa;
35
  font-family: 'Poppins', sans-serif;
36
- }
37
- h1, h2, h3 {
38
- color: #008080;
39
  font-weight: 700;
40
- }
41
- h1 {
42
  font-size: 3em;
43
  text-align: center;
44
  margin-bottom: 20px;
45
- }
46
- h2 {
47
  font-size: 2.2em;
48
  margin-top: 20px;
49
- }
50
- h3 {
51
  font-size: 1.8em;
52
- }
53
- .stApp {
 
54
  padding: 20px;
55
  border-radius: 10px;
56
- background-color: #f5f7fa;
57
- }
58
- .stButton>button {
59
- background-color: #008080;
 
 
 
60
  color: white;
61
  border: none;
62
  padding: 12px 24px;
63
  font-size: 18px;
64
  border-radius: 10px;
65
- transition: background-color 0.3s;
66
- }
67
- .stButton>button:hover {
68
- background-color: #006666;
69
- }
70
- .stTextInput>div>div>input {
 
71
  border-radius: 10px;
72
  border: 1px solid #ccc;
73
  padding: 12px;
74
  font-size: 18px;
75
- }
76
- .stSidebar > div {
77
  background-color: rgba(255, 255, 255, 0.95);
78
  padding: 20px;
79
  border-radius: 10px;
80
- }
81
- .chat-message {
82
  font-size: 18px;
83
  font-weight: bold;
84
  color: #008080;
85
- }
86
  </style>
87
  """,
88
  unsafe_allow_html=True
89
  )
90
 
91
- # Sidebar with option menu
92
  selected_option = option_menu(
93
- menu_title="🌎 TerraPulse",
94
  options=["Home", "Waste-wise", "EcoRoute: Sustainable Travel Planner"],
95
  icons=["house", "recycle", "globe"],
96
- menu_icon="cast",
97
  default_index=0,
98
  orientation="horizontal",
99
  styles={
100
- "container": {"padding": "5!important", "background-color": "#e0f7fa"},
101
- "icon": {"color": "#006666", "font-size": "25px"},
102
- "nav-link": {"font-size": "20px", "text-align": "center", "margin":"0px", "--hover-color": "#e0f7fa"},
103
- "nav-link-selected": {"background-color": "#008080"},
104
  }
105
  )
106
 
107
- # Home page
108
  if selected_option == "Home":
109
  st.title("🌍 Welcome to TerraPulse")
110
  st.markdown(
111
  """
112
  **TerraPulse** is your go-to application for a sustainable future. 🌱
113
  Whether you're looking to classify waste for proper disposal or planning an eco-friendly route for your next trip, TerraPulse has got you covered.
 
114
  **Features:**
115
  - **♻️ Waste-wise:** Upload images of trash items, and TerraPulse will classify them into recyclables, compostables, hazardous materials, and general waste.
116
  - **🌍 EcoRoute:** Plan your travel with the environment in mind. Get the most sustainable routes, transportation suggestions, and carbon footprint estimates.
 
117
  **Let's work together for a cleaner and greener planet!** 🌍💚
118
  """
119
  )
120
 
121
- # Load Gemini Pro Vision model
122
  @st.cache_resource
123
- def load_model():
124
- if not GOOGLE_API_KEY:
125
- st.error("Google API Key not found in .env file.")
 
 
 
 
 
 
 
 
126
  st.stop()
127
- genai.configure(api_key=GOOGLE_API_KEY)
128
- return genai.GenerativeModel('gemini-1.5-flash')
129
 
130
- # Analyze image function
131
- def analyze_image(image, prompt):
132
- model = load_model()
133
  try:
134
  response = model.generate_content([prompt, image])
135
  return response.text
136
  except Exception as e:
137
  st.error(f"An error occurred during analysis: {str(e)}")
138
  return None
139
-
140
  def parse_modes_and_footprints(response_text):
141
- # Regular expression to match the rows of the table
142
  row_pattern = re.compile(r'\| (.+?) \| ([\d.]+) \|')
143
-
144
- # Find all rows in the table
145
  matches = row_pattern.findall(response_text)
146
-
147
- modes = []
148
- carbon_footprints = []
149
-
150
  for match in matches:
151
- mode = match[0].strip()
152
- footprint = float(match[1].strip())
153
- modes.append(mode)
154
- carbon_footprints.append(footprint)
155
-
156
  if not modes or not carbon_footprints:
157
  raise ValueError("No valid data found in the response text")
158
-
159
  return modes, carbon_footprints
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
- # Waste-wise section
163
  if selected_option == "Waste-wise":
164
  st.title("♻️ Waste-wise")
165
-
166
  st.subheader("📤 Upload Image")
167
  uploaded_files = st.file_uploader("Choose trash images...", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
168
-
169
  prompt = "Analyze the image of trash items. Classify the waste into categories such as recyclables, compostables, hazardous materials, and general waste. Based on the classification, guide the user on which specific color dustbin (e.g., recycling, compost, hazardous, or landfill) to dispose of the items."
170
 
171
  if uploaded_files:
172
  analyze_button = st.button("🔍 Analyze Image")
173
  for uploaded_file in uploaded_files:
174
  col1, col2 = st.columns(2)
175
-
176
  with col1:
177
  st.subheader("🖼️ Uploaded Image")
178
  image = PILImage.open(uploaded_file)
179
  st.image(image, caption="Uploaded Image", use_column_width=True)
180
-
181
  with col2:
182
  st.subheader("🧠 Image Analysis")
183
  if analyze_button:
184
  with st.spinner("Analyzing the image..."):
185
- analysis = analyze_image(image, prompt)
186
  if analysis:
187
  st.markdown(analysis)
188
  else:
189
  st.info("Click 'Analyze Image' to start the analysis.")
190
 
191
- # EcoRoute section
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  if selected_option == "EcoRoute: Sustainable Travel Planner":
193
- st.title("🌍 EcoRoute: Sustainable Travel Planner")
194
 
195
- gmaps = googlemaps.Client(key=MAP_API_KEY)
196
- model = load_model()
 
197
 
198
- # User inputs
199
  start_location = st.text_input("Enter your start location")
200
  destination_location = st.text_input("Enter your destination location")
201
- no_of_people = st.selectbox(
202
- "Choose the number of people",
203
- ["1", "2", "3-6", "6-10", "10+"]
204
- )
205
-
206
- def calculate_trees(carbon_footprint):
207
- """Calculate the number of trees required to offset the carbon footprint."""
208
- carbon_per_tree = 0.02177 # Metric tons of CO₂ absorbed per year by one tree
209
- return carbon_footprint / carbon_per_tree
210
 
211
  if st.button("Find Eco-Friendly Route"):
212
  if start_location and destination_location:
213
- # Geocoding the start and end locations
214
- geocode_start = gmaps.geocode(start_location)
215
- geocode_end = gmaps.geocode(destination_location)
216
-
217
- if geocode_start and geocode_end:
218
- start_coords = geocode_start[0]['geometry']['location']
219
- end_coords = geocode_end[0]['geometry']['location']
220
-
221
- # Prompt Google Gemini API to suggest an eco-friendly mode of transport
222
- prompt = f"""You are an eco-friendly mode of transport suggestor. Your job is to provide me with the best routes in bullet points in a comprehensive manner. Suggest the most eco-friendly mode of transport between {start_location} and {destination_location}. Here is the number of people traveling: {no_of_people}. Consider all the above parameters to provide the result in the below format:
223
- Distance: [distance]
224
- Mode 1: Train
225
- - Time: 2h 30m
226
- - Feasibility: High
227
- - Route: Detailed route description
228
- - Carbon footprint (unit): 30.0
229
- Mode 2: Bus
230
- - Time: 3h 00m
231
- - Feasibility: Medium
232
- - Route: Detailed route description
233
- - Carbon footprint (unit): 50.0
234
- Mode 3: Car
235
- - Time: 1h 15m
236
- - Feasibility: High
237
- - Route: Detailed route description
238
- - Carbon footprint (unit): 120.0
239
- Use this exact format. carbon footprint output should only be a single number in float format, nothing else. Generate a table of the different modes of transport vs their carbon footprint.
240
- Similarly, using your own knowledge, provide eco-friendly routes and the most eco-friendly option along with the estimated carbon footprint.
241
- """
242
 
243
- response = model.generate_content([prompt])
244
- eco_friendly_modes = response.text.strip()
 
 
245
 
246
- st.write(f"**Suggested Eco-Friendly Modes:**\n{eco_friendly_modes}")
 
 
 
 
 
 
 
 
 
 
247
 
248
  try:
249
- modes, carbon_footprints = parse_modes_and_footprints(eco_friendly_modes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
- # Plotting the pie chart using Plotly
252
  if modes and carbon_footprints:
253
  fig = go.Figure(data=[go.Pie(labels=modes, values=carbon_footprints)])
254
  fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=20)
255
  fig.update_layout(title="Carbon Footprint Distribution by Mode of Transport", margin=dict(l=0, r=0, t=40, b=0))
256
 
257
- # Adjust layout to ensure map and pie chart do not overlap
258
  col1, col2 = st.columns([2, 1])
259
-
260
  with col1:
261
- # Displaying the map
262
  st.subheader("EcoRoute Map View")
263
- # Create a map centered at the midpoint
264
  midpoint = [(start_coords['lat'] + end_coords['lat']) / 2, (start_coords['lng'] + end_coords['lng']) / 2]
265
- m = folium.Map(location=midpoint, zoom_start=8)
266
 
 
 
 
 
 
 
267
  folium.Marker([start_coords['lat'], start_coords['lng']], popup=start_location, icon=folium.Icon(color='green')).add_to(m)
268
  folium.Marker([end_coords['lat'], end_coords['lng']], popup=destination_location, icon=folium.Icon(color='red')).add_to(m)
269
-
270
- # Draw a line between the start and end locations
271
  folium.PolyLine(locations=[(start_coords['lat'], start_coords['lng']), (end_coords['lat'], end_coords['lng'])], color='blue').add_to(m)
272
-
273
  folium_static(m)
274
 
275
  with col2:
276
  st.plotly_chart(fig, use_container_width=True)
277
-
278
- except ValueError as e:
279
- st.error(f"Error parsing the response: {str(e)}")
280
-
281
  else:
282
- st.error("Could not geocode one or both locations. Please check the input.")
283
  else:
284
- st.error("Please enter both the start and destination locations.")
 
 
1
  import streamlit as st
2
  import google.generativeai as genai
3
+ import requests
4
  from datetime import datetime
5
  from PIL import Image as PILImage
6
  import folium
7
  from streamlit_folium import folium_static
8
  import os
 
9
  from streamlit_option_menu import option_menu
10
  import plotly.graph_objs as go
11
+ import base64
12
  import re
13
 
 
 
 
14
  # Initialize APIs
15
+ MAPTILER_API_KEY = "bK9j55GlT3HtekZ95TkH"
 
16
 
17
  # Configure the page
18
  st.set_page_config(
 
22
  initial_sidebar_state="expanded"
23
  )
24
 
25
+ def get_base64_image(image_path):
26
+ with open(image_path, "rb") as img_file:
27
+ return base64.b64encode(img_file.read()).decode()
28
+
29
+ img_base64 = get_base64_image("bg.jpg")
30
+
31
  st.markdown(
32
+ f"""
33
  <style>
34
+ header {{visibility: hidden;}}
35
+ footer {{visibility: hidden;}}
36
+ body {{
37
  background-color: #f5f7fa;
38
  font-family: 'Poppins', sans-serif;
39
+ }}
40
+ h1, h2, h3 {{
41
+ color: #00287a;
42
  font-weight: 700;
43
+ }}
44
+ h1 {{
45
  font-size: 3em;
46
  text-align: center;
47
  margin-bottom: 20px;
48
+ }}
49
+ h2 {{
50
  font-size: 2.2em;
51
  margin-top: 20px;
52
+ }}
53
+ h3 {{
54
  font-size: 1.8em;
55
+ }}
56
+ .stApp {{
57
+ min-height: 100vh;
58
  padding: 20px;
59
  border-radius: 10px;
60
+ background-image: url("data:image/jpeg;base64,{img_base64}");
61
+ background-size: cover;
62
+ background-position: center;
63
+ background-repeat: no-repeat;
64
+ }}
65
+ .stButton>button {{
66
+ background-color: #00a05c;
67
  color: white;
68
  border: none;
69
  padding: 12px 24px;
70
  font-size: 18px;
71
  border-radius: 10px;
72
+ transition: background-color 0.3s, color 0.3s;
73
+ }}
74
+ .stButton>button:hover {{
75
+ background-color: #00bd4b;
76
+ color: #ffffff;
77
+ }}
78
+ .stTextInput>div>div>input {{
79
  border-radius: 10px;
80
  border: 1px solid #ccc;
81
  padding: 12px;
82
  font-size: 18px;
83
+ }}
84
+ .stSidebar > div {{
85
  background-color: rgba(255, 255, 255, 0.95);
86
  padding: 20px;
87
  border-radius: 10px;
88
+ }}
89
+ .chat-message {{
90
  font-size: 18px;
91
  font-weight: bold;
92
  color: #008080;
93
+ }}
94
  </style>
95
  """,
96
  unsafe_allow_html=True
97
  )
98
 
 
99
  selected_option = option_menu(
100
+ menu_title="TerraPulse",
101
  options=["Home", "Waste-wise", "EcoRoute: Sustainable Travel Planner"],
102
  icons=["house", "recycle", "globe"],
103
+ menu_icon="globe",
104
  default_index=0,
105
  orientation="horizontal",
106
  styles={
107
+ "container": {"padding": "5!important", "background-color": "#ffffff00"},
108
+ "icon": {"color": "#000000", "font-size": "25px"},
109
+ "nav-link": {"font-size": "18px", "text-align": "center", "--hover-color": "#ffffff50", "border-radius": "12px", "font-family": "'Segoe UI', sans-serif", "padding": "4px 20px 8px 20px",},
110
+ "nav-link-selected": {"background-color": "#00845873", "border-radius": "12px", "font-family": "'Segoe UI', sans-serif", "padding": "4px 20px 10px 20px",},
111
  }
112
  )
113
 
 
114
  if selected_option == "Home":
115
  st.title("🌍 Welcome to TerraPulse")
116
  st.markdown(
117
  """
118
  **TerraPulse** is your go-to application for a sustainable future. 🌱
119
  Whether you're looking to classify waste for proper disposal or planning an eco-friendly route for your next trip, TerraPulse has got you covered.
120
+
121
  **Features:**
122
  - **♻️ Waste-wise:** Upload images of trash items, and TerraPulse will classify them into recyclables, compostables, hazardous materials, and general waste.
123
  - **🌍 EcoRoute:** Plan your travel with the environment in mind. Get the most sustainable routes, transportation suggestions, and carbon footprint estimates.
124
+
125
  **Let's work together for a cleaner and greener planet!** 🌍💚
126
  """
127
  )
128
 
 
129
  @st.cache_resource
130
+ def load_model(api_key):
131
+ if not api_key:
132
+ st.error("Please Enter Google API Key")
133
+ st.stop()
134
+ try:
135
+ genai.configure(api_key=api_key)
136
+ model = genai.GenerativeModel('gemini-1.5-flash')
137
+ model.generate_content("Ping")
138
+ return model
139
+ except:
140
+ st.error("Please Enter Valid API Key")
141
  st.stop()
 
 
142
 
143
+ def analyze_image(image, prompt, api_key):
144
+ model = load_model(api_key)
 
145
  try:
146
  response = model.generate_content([prompt, image])
147
  return response.text
148
  except Exception as e:
149
  st.error(f"An error occurred during analysis: {str(e)}")
150
  return None
151
+
152
  def parse_modes_and_footprints(response_text):
 
153
  row_pattern = re.compile(r'\| (.+?) \| ([\d.]+) \|')
 
 
154
  matches = row_pattern.findall(response_text)
155
+ modes, carbon_footprints = [], []
 
 
 
156
  for match in matches:
157
+ modes.append(match[0].strip())
158
+ carbon_footprints.append(float(match[1].strip()))
 
 
 
159
  if not modes or not carbon_footprints:
160
  raise ValueError("No valid data found in the response text")
 
161
  return modes, carbon_footprints
162
 
163
+ def geocode_location_maptiler(location):
164
+ if not MAPTILER_API_KEY:
165
+ st.error("MapTiler API key missing. Please add it to your .env file.")
166
+ return None
167
+ response = requests.get(
168
+ f"https://api.maptiler.com/geocoding/{location}.json?key={MAPTILER_API_KEY}"
169
+ )
170
+ if response.status_code != 200:
171
+ st.error(f"MapTiler Geocoding API error: {response.status_code}")
172
+ return None
173
+ data = response.json()
174
+ if data.get("features"):
175
+ coords = data["features"][0]["geometry"]["coordinates"]
176
+ return {"lat": coords[1], "lng": coords[0]}
177
+ return None
178
 
 
179
  if selected_option == "Waste-wise":
180
  st.title("♻️ Waste-wise")
181
+ api_key = st.text_input("Enter your Google API key:", type="password")
182
  st.subheader("📤 Upload Image")
183
  uploaded_files = st.file_uploader("Choose trash images...", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
 
184
  prompt = "Analyze the image of trash items. Classify the waste into categories such as recyclables, compostables, hazardous materials, and general waste. Based on the classification, guide the user on which specific color dustbin (e.g., recycling, compost, hazardous, or landfill) to dispose of the items."
185
 
186
  if uploaded_files:
187
  analyze_button = st.button("🔍 Analyze Image")
188
  for uploaded_file in uploaded_files:
189
  col1, col2 = st.columns(2)
 
190
  with col1:
191
  st.subheader("🖼️ Uploaded Image")
192
  image = PILImage.open(uploaded_file)
193
  st.image(image, caption="Uploaded Image", use_column_width=True)
 
194
  with col2:
195
  st.subheader("🧠 Image Analysis")
196
  if analyze_button:
197
  with st.spinner("Analyzing the image..."):
198
+ analysis = analyze_image(image, prompt, api_key)
199
  if analysis:
200
  st.markdown(analysis)
201
  else:
202
  st.info("Click 'Analyze Image' to start the analysis.")
203
 
204
+ def parse_modes_and_footprints(response_text):
205
+ lines = response_text.strip().split('\n')
206
+ modes = []
207
+ carbon_footprints = []
208
+
209
+ for line in lines:
210
+ line = line.strip()
211
+ if (line.startswith('|') and '-' in line) or line.lower().startswith('| mode'):
212
+ continue
213
+
214
+ if line.startswith('|') and line.endswith('|'):
215
+ parts = [part.strip() for part in line.strip('|').split('|')]
216
+ if len(parts) >= 2:
217
+ mode = parts[0]
218
+ footprint_str = parts[1]
219
+ try:
220
+ footprint = float(footprint_str)
221
+ modes.append(mode)
222
+ carbon_footprints.append(footprint)
223
+ except ValueError:
224
+ continue
225
+
226
+ if not modes or not carbon_footprints:
227
+ raise ValueError("No valid data found in the response text")
228
+
229
+ return modes, carbon_footprints
230
+
231
  if selected_option == "EcoRoute: Sustainable Travel Planner":
 
232
 
233
+ api_key = st.text_input("Enter your Google API key:", type="password")
234
+ st.title("🌍 EcoRoute: Sustainable Travel Planner")
235
+ model = load_model(api_key)
236
 
 
237
  start_location = st.text_input("Enter your start location")
238
  destination_location = st.text_input("Enter your destination location")
239
+ no_of_people = st.selectbox("Choose the number of people", ["1", "2", "3-6", "6-10", "10+"])
 
 
 
 
 
 
 
 
240
 
241
  if st.button("Find Eco-Friendly Route"):
242
  if start_location and destination_location:
243
+ start_coords = geocode_location_maptiler(start_location)
244
+ end_coords = geocode_location_maptiler(destination_location)
245
+
246
+ if start_coords and end_coords:
247
+ prompt = f"""
248
+ You are an eco-friendly travel advisor.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
+ Given:
251
+ - Start location: {start_location}
252
+ - Destination location: {destination_location}
253
+ - Number of people traveling: {no_of_people}
254
 
255
+ First, write 2-3 sentences recommending the most sustainable option and explaining briefly why it's preferred. Then, respond with a markdown table listing transport modes and their estimated carbon footprints (in kg CO2e) per passenger, formatted exactly like this:
256
+
257
+ | Mode | Carbon Footprint |
258
+ |--------------|------------------|
259
+ | Walking | 0 |
260
+ | Cycling | 0 |
261
+ | Train | 15 |
262
+ | Electric Car | 10 |
263
+
264
+ Make sure the table comes **after** the recommendation text.
265
+ """
266
 
267
  try:
268
+ response = model.generate_content([prompt])
269
+ eco_friendly_modes = response.text.strip()
270
+
271
+ # Split into recommendation and table
272
+ table_start = eco_friendly_modes.find("| Mode")
273
+ if table_start == -1:
274
+ raise ValueError("No markdown table found in the response.")
275
+
276
+ recommendation_text = eco_friendly_modes[:table_start].strip()
277
+ markdown_table = eco_friendly_modes[table_start:].strip()
278
+
279
+ st.markdown(f"**AI Recommendation:**\n\n{recommendation_text}")
280
+ st.markdown(markdown_table)
281
+
282
+ # Parse the markdown table
283
+ modes, carbon_footprints = parse_modes_and_footprints(markdown_table)
284
 
 
285
  if modes and carbon_footprints:
286
  fig = go.Figure(data=[go.Pie(labels=modes, values=carbon_footprints)])
287
  fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=20)
288
  fig.update_layout(title="Carbon Footprint Distribution by Mode of Transport", margin=dict(l=0, r=0, t=40, b=0))
289
 
 
290
  col1, col2 = st.columns([2, 1])
 
291
  with col1:
 
292
  st.subheader("EcoRoute Map View")
 
293
  midpoint = [(start_coords['lat'] + end_coords['lat']) / 2, (start_coords['lng'] + end_coords['lng']) / 2]
 
294
 
295
+ m = folium.Map(
296
+ location=midpoint,
297
+ zoom_start=8,
298
+ tiles=f"https://api.maptiler.com/maps/streets-v2/256/{{z}}/{{x}}/{{y}}.png?key={MAPTILER_API_KEY}",
299
+ attr="MapTiler"
300
+ )
301
  folium.Marker([start_coords['lat'], start_coords['lng']], popup=start_location, icon=folium.Icon(color='green')).add_to(m)
302
  folium.Marker([end_coords['lat'], end_coords['lng']], popup=destination_location, icon=folium.Icon(color='red')).add_to(m)
 
 
303
  folium.PolyLine(locations=[(start_coords['lat'], start_coords['lng']), (end_coords['lat'], end_coords['lng'])], color='blue').add_to(m)
 
304
  folium_static(m)
305
 
306
  with col2:
307
  st.plotly_chart(fig, use_container_width=True)
308
+
309
+ except Exception as e:
310
+ st.error(f"Error processing the eco-route suggestion: {str(e)}")
 
311
  else:
312
+ st.error("Could not geocode one or both locations. Please check the location names.")
313
  else:
314
+ st.warning("Please enter both start and destination locations.")
315
+