Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -70,23 +70,43 @@ def find_fastest_route(G, start_node, end_node, weight='travel_time'):
|
|
| 70 |
except nx.NetworkXNoPath:
|
| 71 |
return None, None
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
def plot_route_on_map(G, route, start_point, end_point):
|
| 74 |
"""Отрисовывает один маршрут на большой цветной карте со стильными маркерами."""
|
| 75 |
-
# Возвращаем цветную карту OpenStreetMap
|
| 76 |
m = folium.Map(tiles="OpenStreetMap")
|
| 77 |
|
| 78 |
if route:
|
| 79 |
points = [(G.nodes[node]['y'], G.nodes[node]['x']) for node in route]
|
| 80 |
-
|
| 81 |
-
# Белая "подложка" для контраста и лучшей видимости
|
| 82 |
folium.PolyLine(points, color="white", weight=10, opacity=0.8).add_to(m)
|
| 83 |
-
# Основная линия маршрута - яркий, приятный синий цвет
|
| 84 |
main_line = folium.PolyLine(points, color="#3b82f6", weight=5, opacity=1, tooltip="Оптимальный маршрут")
|
| 85 |
main_line.add_to(m)
|
| 86 |
-
|
| 87 |
m.fit_bounds(main_line.get_bounds(), padding=(30, 30))
|
| 88 |
|
| 89 |
-
# Новые, более стильные маркеры с использованием иконок Font Awesome
|
| 90 |
folium.Marker(
|
| 91 |
location=start_point, popup="<b>Точка А (Старт)</b>",
|
| 92 |
icon=folium.Icon(color="blue", icon="play-circle", prefix='fa')
|
|
@@ -105,7 +125,7 @@ print("✅ Вспомогательные функции готовы.")
|
|
| 105 |
# ==============================================================================
|
| 106 |
print("\n🚀 Шаг 5: Настройка и запуск веб-интерфейса Gradio...")
|
| 107 |
|
| 108 |
-
geolocator = Nominatim(user_agent="
|
| 109 |
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
|
| 110 |
|
| 111 |
def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour, minute):
|
|
@@ -124,7 +144,10 @@ def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour
|
|
| 124 |
start_node = ox.nearest_nodes(G, Y=start_point[0], X=start_point[1])
|
| 125 |
end_node = ox.nearest_nodes(G, Y=end_point[0], X=end_point[1])
|
| 126 |
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
| 128 |
travel_times = predict_graph_weights(gdf_edges, model, selected_time)
|
| 129 |
nx.set_edge_attributes(G, values=pd.Series(travel_times, index=gdf_edges.index).to_dict(), name='travel_time')
|
| 130 |
|
|
@@ -132,7 +155,9 @@ def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour
|
|
| 132 |
if route is None:
|
| 133 |
return None, "### ❌ Ошибка\nНе удалось построить маршрут. Возможно, точки находятся в несвязанных частях города."
|
| 134 |
|
| 135 |
-
|
|
|
|
|
|
|
| 136 |
signals_on_route = sum(1 for node in route if node in signal_nodes_set)
|
| 137 |
total_time_sec = travel_time + signals_on_route * SIGNAL_DELAY_SECONDS
|
| 138 |
total_time_min = total_time_sec / 60
|
|
@@ -140,12 +165,12 @@ def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour
|
|
| 140 |
|
| 141 |
final_map = plot_route_on_map(G, route, start_point, end_point)
|
| 142 |
|
| 143 |
-
# Форматирование вывода в Markdown с новой цветовой схемой
|
| 144 |
output_md = f"""
|
| 145 |
### ✅ Маршрут построен!
|
| 146 |
- **Время в пути:** <span style="font-size: 1.1em; color: #3b82f6;">**~{total_time_min:.1f} мин.**</span>
|
| 147 |
- **Расстояние:** **{distance_km:.2f} км**
|
| 148 |
-
- **Светофоров на пути:** **{signals_on_route}**
|
|
|
|
| 149 |
"""
|
| 150 |
return final_map._repr_html_(), output_md
|
| 151 |
|
|
@@ -156,7 +181,6 @@ def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour
|
|
| 156 |
css = """
|
| 157 |
body { font-family: 'Inter', sans-serif; }
|
| 158 |
.gradio-container { max-width: 100% !important; }
|
| 159 |
-
/* Кнопка */
|
| 160 |
.gr-button-primary {
|
| 161 |
background: linear-gradient(to right, #60a5fa, #3b82f6);
|
| 162 |
color: white; font-weight: bold; border: none; transition: all 0.3s ease;
|
|
@@ -166,7 +190,6 @@ body { font-family: 'Inter', sans-serif; }
|
|
| 166 |
background: linear-gradient(to right, #3b82f6, #2563eb);
|
| 167 |
box-shadow: 0 6px 20px 0 rgba(59, 130, 246, 0.45); transform: translateY(-1px);
|
| 168 |
}
|
| 169 |
-
/* Информационный блок */
|
| 170 |
#info-box { background-color: #f9fafb; padding: 1.5rem !important; border-radius: 0.75rem; border: 1px solid #e5e7eb;}
|
| 171 |
"""
|
| 172 |
|
|
@@ -185,7 +208,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="blue", fo
|
|
| 185 |
start_address_input = gr.Textbox(label="Откуда?", placeholder="Например, ЦУМ")
|
| 186 |
end_address_input = gr.Textbox(label="Куда?", placeholder="Например, парк Ата-Тюрк")
|
| 187 |
|
| 188 |
-
# Убрали Accordion, теперь настройки времени всегда видны
|
| 189 |
gr.Markdown("#### 🗓️ Укажите время и дату")
|
| 190 |
day_dropdown = gr.Dropdown(label="День недели", choices=["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"], value="Понедельник", type="index")
|
| 191 |
current_time = datetime.datetime.now()
|
|
@@ -195,7 +217,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="blue", fo
|
|
| 195 |
build_btn = gr.Button("🚀 Найти лучший маршрут", variant="primary")
|
| 196 |
output_info = gr.Markdown(label="Информация о маршруте", elem_id="info-box")
|
| 197 |
|
| 198 |
-
with gr.Column(scale=3):
|
| 199 |
output_map_html = gr.HTML(label="Карта с маршрутом", elem_id="map-output")
|
| 200 |
|
| 201 |
build_btn.click(
|
|
|
|
| 70 |
except nx.NetworkXNoPath:
|
| 71 |
return None, None
|
| 72 |
|
| 73 |
+
# --- ИЗМЕНЕНИЕ ЛОГИКИ ЗДЕСЬ ---
|
| 74 |
+
def get_signal_delay(day_of_week, hour):
|
| 75 |
+
"""Определяет задержку на светофоре согласно новым правилам."""
|
| 76 |
+
is_weekday = 0 <= day_of_week <= 4 # Понедельник (0) - Пятница (4)
|
| 77 |
+
|
| 78 |
+
# Правило 1: Часы-пик в будние дни (высший приоритет)
|
| 79 |
+
is_morning_rush = 8 <= hour <= 10
|
| 80 |
+
is_evening_rush = 17 <= hour <= 20
|
| 81 |
+
if is_weekday and (is_morning_rush or is_evening_rush):
|
| 82 |
+
return 35
|
| 83 |
+
|
| 84 |
+
# Правило 2: Ночное время, любой день (второй приоритет)
|
| 85 |
+
# "до 8 часов утра" означает до 7:59, поэтому "hour <= 7"
|
| 86 |
+
is_night_time = hour >= 23 or hour <= 7
|
| 87 |
+
if is_night_time:
|
| 88 |
+
return 15
|
| 89 |
+
|
| 90 |
+
# Правило 3: Дневное время в будни (не час-пик и не ночь)
|
| 91 |
+
if is_weekday:
|
| 92 |
+
return 25
|
| 93 |
+
|
| 94 |
+
# Правило 4: Дневное время в выходные (не ночь)
|
| 95 |
+
# Если код дошел до сюда, значит это выходной день (is_weekday = False)
|
| 96 |
+
else:
|
| 97 |
+
return 15
|
| 98 |
+
|
| 99 |
def plot_route_on_map(G, route, start_point, end_point):
|
| 100 |
"""Отрисовывает один маршрут на большой цветной карте со стильными маркерами."""
|
|
|
|
| 101 |
m = folium.Map(tiles="OpenStreetMap")
|
| 102 |
|
| 103 |
if route:
|
| 104 |
points = [(G.nodes[node]['y'], G.nodes[node]['x']) for node in route]
|
|
|
|
|
|
|
| 105 |
folium.PolyLine(points, color="white", weight=10, opacity=0.8).add_to(m)
|
|
|
|
| 106 |
main_line = folium.PolyLine(points, color="#3b82f6", weight=5, opacity=1, tooltip="Оптимальный маршрут")
|
| 107 |
main_line.add_to(m)
|
|
|
|
| 108 |
m.fit_bounds(main_line.get_bounds(), padding=(30, 30))
|
| 109 |
|
|
|
|
| 110 |
folium.Marker(
|
| 111 |
location=start_point, popup="<b>Точка А (Старт)</b>",
|
| 112 |
icon=folium.Icon(color="blue", icon="play-circle", prefix='fa')
|
|
|
|
| 125 |
# ==============================================================================
|
| 126 |
print("\n🚀 Шаг 5: Настройка и запуск веб-интерфейса Gradio...")
|
| 127 |
|
| 128 |
+
geolocator = Nominatim(user_agent="bishkek_navigator_app_v7")
|
| 129 |
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
|
| 130 |
|
| 131 |
def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour, minute):
|
|
|
|
| 144 |
start_node = ox.nearest_nodes(G, Y=start_point[0], X=start_point[1])
|
| 145 |
end_node = ox.nearest_nodes(G, Y=end_point[0], X=end_point[1])
|
| 146 |
|
| 147 |
+
current_hour = int(hour)
|
| 148 |
+
current_minute = int(minute)
|
| 149 |
+
|
| 150 |
+
selected_time = datetime.datetime(2023, 1, 2 + day_of_week, current_hour, current_minute)
|
| 151 |
travel_times = predict_graph_weights(gdf_edges, model, selected_time)
|
| 152 |
nx.set_edge_attributes(G, values=pd.Series(travel_times, index=gdf_edges.index).to_dict(), name='travel_time')
|
| 153 |
|
|
|
|
| 155 |
if route is None:
|
| 156 |
return None, "### ❌ Ошибка\nНе удалось построить маршрут. Возможно, точки находятся в несвязанных частях города."
|
| 157 |
|
| 158 |
+
# Получаем динамическую задержку на светофоре согласно новым правилам
|
| 159 |
+
SIGNAL_DELAY_SECONDS = get_signal_delay(day_of_week, current_hour)
|
| 160 |
+
|
| 161 |
signals_on_route = sum(1 for node in route if node in signal_nodes_set)
|
| 162 |
total_time_sec = travel_time + signals_on_route * SIGNAL_DELAY_SECONDS
|
| 163 |
total_time_min = total_time_sec / 60
|
|
|
|
| 165 |
|
| 166 |
final_map = plot_route_on_map(G, route, start_point, end_point)
|
| 167 |
|
|
|
|
| 168 |
output_md = f"""
|
| 169 |
### ✅ Маршрут построен!
|
| 170 |
- **Время в пути:** <span style="font-size: 1.1em; color: #3b82f6;">**~{total_time_min:.1f} мин.**</span>
|
| 171 |
- **Расстояние:** **{distance_km:.2f} км**
|
| 172 |
+
- **Светофоров на пути:** **{signals_on_route}**
|
| 173 |
+
<p style="font-size: 0.8em; color: #6b7280;">(задержка на светофор: {SIGNAL_DELAY_SECONDS} сек.)</p>
|
| 174 |
"""
|
| 175 |
return final_map._repr_html_(), output_md
|
| 176 |
|
|
|
|
| 181 |
css = """
|
| 182 |
body { font-family: 'Inter', sans-serif; }
|
| 183 |
.gradio-container { max-width: 100% !important; }
|
|
|
|
| 184 |
.gr-button-primary {
|
| 185 |
background: linear-gradient(to right, #60a5fa, #3b82f6);
|
| 186 |
color: white; font-weight: bold; border: none; transition: all 0.3s ease;
|
|
|
|
| 190 |
background: linear-gradient(to right, #3b82f6, #2563eb);
|
| 191 |
box-shadow: 0 6px 20px 0 rgba(59, 130, 246, 0.45); transform: translateY(-1px);
|
| 192 |
}
|
|
|
|
| 193 |
#info-box { background-color: #f9fafb; padding: 1.5rem !important; border-radius: 0.75rem; border: 1px solid #e5e7eb;}
|
| 194 |
"""
|
| 195 |
|
|
|
|
| 208 |
start_address_input = gr.Textbox(label="Откуда?", placeholder="Например, ЦУМ")
|
| 209 |
end_address_input = gr.Textbox(label="Куда?", placeholder="Например, парк Ата-Тюрк")
|
| 210 |
|
|
|
|
| 211 |
gr.Markdown("#### 🗓️ Укажите время и дату")
|
| 212 |
day_dropdown = gr.Dropdown(label="День недели", choices=["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"], value="Понедельник", type="index")
|
| 213 |
current_time = datetime.datetime.now()
|
|
|
|
| 217 |
build_btn = gr.Button("🚀 Найти лучший маршрут", variant="primary")
|
| 218 |
output_info = gr.Markdown(label="Информация о маршруте", elem_id="info-box")
|
| 219 |
|
| 220 |
+
with gr.Column(scale=3):
|
| 221 |
output_map_html = gr.HTML(label="Карта с маршрутом", elem_id="map-output")
|
| 222 |
|
| 223 |
build_btn.click(
|