Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -60,39 +60,23 @@ def calculate_real_distances(places):
|
|
60 |
return distances, routing_info
|
61 |
|
62 |
def optimize_route(distances):
|
63 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
row_ind, col_ind = linear_sum_assignment(distances)
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
return None
|
75 |
-
|
76 |
-
def create_numbered_marker(number):
|
77 |
-
"""Create a custom marker with a number"""
|
78 |
-
return plugins.BeautifyIcon(
|
79 |
-
number=number,
|
80 |
-
border_color='#b4b4b4',
|
81 |
-
border_width=1,
|
82 |
-
text_color='black',
|
83 |
-
background_color='white',
|
84 |
-
inner_icon_style='margin-top:0;'
|
85 |
-
)
|
86 |
-
|
87 |
-
def format_instructions(steps):
|
88 |
-
"""Format routing instructions from OSRM steps"""
|
89 |
-
instructions = []
|
90 |
-
for step in steps:
|
91 |
-
if 'maneuver' in step:
|
92 |
-
instruction = step.get('maneuver', {}).get('instruction', '')
|
93 |
-
if instruction:
|
94 |
-
instructions.append(instruction)
|
95 |
-
return instructions
|
96 |
|
97 |
def main():
|
98 |
st.title("AI Travel Route Optimizer (with OSRM)")
|
@@ -115,42 +99,42 @@ def main():
|
|
115 |
|
116 |
# Only proceed if we have all places
|
117 |
if len(places) == num_places and num_places >= 2:
|
118 |
-
|
119 |
with st.spinner("Calculating optimal route..."):
|
120 |
# Calculate real distances
|
121 |
distances, routing_info = calculate_real_distances(places)
|
122 |
|
123 |
-
#
|
124 |
-
|
125 |
|
126 |
# Create map
|
127 |
center_lat = sum(coord[0] for _, coord in places) / len(places)
|
128 |
center_lon = sum(coord[1] for _, coord in places) / len(places)
|
129 |
-
|
130 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=4)
|
131 |
|
132 |
-
#
|
133 |
-
|
|
|
134 |
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
next_idx = optimized_indices[(i + 1) % len(optimized_indices)]
|
139 |
|
140 |
-
#
|
141 |
-
|
142 |
-
next_place = places[next_idx]
|
143 |
|
144 |
# Get routing info for this segment
|
145 |
segment_info = routing_info.get((current_idx, next_idx), {})
|
146 |
|
147 |
-
#
|
148 |
popup_content = f"""
|
149 |
-
<b>Stop {i+1}: {
|
150 |
-
To next stop:<br>
|
151 |
-
Distance: {segment_info.get('distance_km', 'N/A')}<br>
|
152 |
-
Duration: {segment_info.get('duration_mins', 'N/A')}
|
153 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
|
155 |
folium.Marker(
|
156 |
[lat, lon],
|
@@ -158,60 +142,58 @@ def main():
|
|
158 |
icon=create_numbered_marker(i+1)
|
159 |
).add_to(m)
|
160 |
|
161 |
-
# Draw route line
|
162 |
-
if
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
# Display map
|
182 |
st.components.v1.html(m._repr_html_(), height=600)
|
183 |
|
184 |
-
# Display optimized itinerary
|
185 |
st.header("Optimized Itinerary")
|
186 |
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
for i in range(len(optimized_indices)):
|
191 |
-
current_idx = optimized_indices[i]
|
192 |
-
next_idx = optimized_indices[(i + 1) % len(optimized_indices)]
|
193 |
|
194 |
-
|
195 |
segment_info = routing_info.get((current_idx, next_idx), {})
|
196 |
|
197 |
-
st.write(f"{i+1}. {
|
198 |
|
199 |
-
if i < len(
|
200 |
st.write(f" ↓ ({segment_info.get('distance_km', 'N/A')}, "
|
201 |
f"Duration: {segment_info.get('duration_mins', 'N/A')})")
|
202 |
|
203 |
-
#
|
204 |
if 'steps' in segment_info:
|
205 |
with st.expander(f"Route instructions to next stop"):
|
206 |
instructions = format_instructions(segment_info['steps'])
|
207 |
for idx, instruction in enumerate(instructions, 1):
|
208 |
st.write(f"{idx}. {instruction}")
|
209 |
-
|
210 |
-
# Add to totals
|
211 |
-
if 'distance_km' in segment_info:
|
212 |
-
total_distance += float(segment_info['distance_km'].split()[0])
|
213 |
-
if 'duration_mins' in segment_info:
|
214 |
-
total_duration += float(segment_info['duration_mins'].split()[0])
|
215 |
|
216 |
# Display totals
|
217 |
st.write("\nSummary:")
|
|
|
60 |
return distances, routing_info
|
61 |
|
62 |
def optimize_route(distances):
|
63 |
+
"""
|
64 |
+
Optimize route using modified Hungarian algorithm for TSP.
|
65 |
+
Returns the optimal order of visits.
|
66 |
+
"""
|
67 |
+
n = len(distances)
|
68 |
+
|
69 |
+
# Solve assignment problem
|
70 |
row_ind, col_ind = linear_sum_assignment(distances)
|
71 |
+
|
72 |
+
# Convert assignment to tour
|
73 |
+
tour = []
|
74 |
+
current = 0 # Start from first city
|
75 |
+
for _ in range(n):
|
76 |
+
tour.append(current)
|
77 |
+
current = col_ind[current]
|
78 |
+
|
79 |
+
return tour
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
def main():
|
82 |
st.title("AI Travel Route Optimizer (with OSRM)")
|
|
|
99 |
|
100 |
# Only proceed if we have all places
|
101 |
if len(places) == num_places and num_places >= 2:
|
|
|
102 |
with st.spinner("Calculating optimal route..."):
|
103 |
# Calculate real distances
|
104 |
distances, routing_info = calculate_real_distances(places)
|
105 |
|
106 |
+
# Get optimized route order
|
107 |
+
optimal_order = optimize_route(distances)
|
108 |
|
109 |
# Create map
|
110 |
center_lat = sum(coord[0] for _, coord in places) / len(places)
|
111 |
center_lon = sum(coord[1] for _, coord in places) / len(places)
|
|
|
112 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=4)
|
113 |
|
114 |
+
# Add markers and route lines following optimal order
|
115 |
+
total_distance = 0
|
116 |
+
total_duration = 0
|
117 |
|
118 |
+
for i in range(len(optimal_order)):
|
119 |
+
current_idx = optimal_order[i]
|
120 |
+
next_idx = optimal_order[(i + 1) % len(optimal_order)]
|
|
|
121 |
|
122 |
+
# Get current and next place info
|
123 |
+
current_place, (lat, lon) = places[current_idx]
|
|
|
124 |
|
125 |
# Get routing info for this segment
|
126 |
segment_info = routing_info.get((current_idx, next_idx), {})
|
127 |
|
128 |
+
# Add marker with proper numbering
|
129 |
popup_content = f"""
|
130 |
+
<b>Stop {i+1}: {current_place}</b><br>
|
|
|
|
|
|
|
131 |
"""
|
132 |
+
if i < len(optimal_order) - 1:
|
133 |
+
popup_content += f"""
|
134 |
+
To next stop:<br>
|
135 |
+
Distance: {segment_info.get('distance_km', 'N/A')}<br>
|
136 |
+
Duration: {segment_info.get('duration_mins', 'N/A')}
|
137 |
+
"""
|
138 |
|
139 |
folium.Marker(
|
140 |
[lat, lon],
|
|
|
142 |
icon=create_numbered_marker(i+1)
|
143 |
).add_to(m)
|
144 |
|
145 |
+
# Draw route line to next point
|
146 |
+
if i < len(optimal_order) - 1:
|
147 |
+
if 'geometry' in segment_info:
|
148 |
+
try:
|
149 |
+
route_coords = polyline.decode(segment_info['geometry'])
|
150 |
+
folium.PolyLine(
|
151 |
+
route_coords,
|
152 |
+
weight=2,
|
153 |
+
color='blue',
|
154 |
+
opacity=0.8
|
155 |
+
).add_to(m)
|
156 |
+
except:
|
157 |
+
next_place = places[next_idx][1]
|
158 |
+
folium.PolyLine(
|
159 |
+
[[lat, lon], [next_place[0], next_place[1]]],
|
160 |
+
weight=2,
|
161 |
+
color='red',
|
162 |
+
opacity=0.8
|
163 |
+
).add_to(m)
|
164 |
+
|
165 |
+
# Add to totals if not last point
|
166 |
+
if i < len(optimal_order) - 1:
|
167 |
+
if 'distance_km' in segment_info:
|
168 |
+
total_distance += float(segment_info['distance_km'].split()[0])
|
169 |
+
if 'duration_mins' in segment_info:
|
170 |
+
total_duration += float(segment_info['duration_mins'].split()[0])
|
171 |
|
172 |
# Display map
|
173 |
st.components.v1.html(m._repr_html_(), height=600)
|
174 |
|
175 |
+
# Display optimized itinerary
|
176 |
st.header("Optimized Itinerary")
|
177 |
|
178 |
+
for i in range(len(optimal_order)):
|
179 |
+
current_idx = optimal_order[i]
|
180 |
+
next_idx = optimal_order[(i + 1) % len(optimal_order)]
|
|
|
|
|
|
|
181 |
|
182 |
+
current_place = places[current_idx][0]
|
183 |
segment_info = routing_info.get((current_idx, next_idx), {})
|
184 |
|
185 |
+
st.write(f"{i+1}. {current_place}")
|
186 |
|
187 |
+
if i < len(optimal_order) - 1:
|
188 |
st.write(f" ↓ ({segment_info.get('distance_km', 'N/A')}, "
|
189 |
f"Duration: {segment_info.get('duration_mins', 'N/A')})")
|
190 |
|
191 |
+
# Show turn-by-turn directions
|
192 |
if 'steps' in segment_info:
|
193 |
with st.expander(f"Route instructions to next stop"):
|
194 |
instructions = format_instructions(segment_info['steps'])
|
195 |
for idx, instruction in enumerate(instructions, 1):
|
196 |
st.write(f"{idx}. {instruction}")
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
|
198 |
# Display totals
|
199 |
st.write("\nSummary:")
|