# ============================================================================== # ШАГ 1: ИМПОРТ И НАСТРОЙКА # ============================================================================== print("🚀 Шаг 1: Импорт всех библиотек...") import osmnx as ox import networkx as nx import pandas as pd import datetime import folium import gradio as gr from geopy.geocoders import Nominatim from geopy.extra.rate_limiter import RateLimiter import pickle from catboost import CatBoostRegressor print("✅ Импорт завершен.") # ============================================================================== # ШАГ 2: ЗАГРУЗКА ПРЕДОБРАБОТАННЫХ ДАННЫХ # ============================================================================== print("\n🌍 Шаг 2: Загрузка данных о городе из 'graph_data.pkl'...") with open("graph_data.pkl", "rb") as f: city_data = pickle.load(f) G = city_data["graph"] gdf_edges = city_data["edges_gdf"] signal_nodes_set = city_data["signal_nodes"] print(f" ✅ Граф ({G.number_of_nodes()} узлов, {G.number_of_edges()} ребер) загружен.") print(f" ✅ Данные о светофорах ({len(signal_nodes_set)} перекрестков) загружены.") print("✅ Данные о городе полностью готовы!") # ============================================================================== # ШАГ 3: ЗАГРУЗКА ОБУЧЕННОЙ МОДЕЛИ # ============================================================================== print("\n🧠 Шаг 3: Загрузка обученной модели предсказания трафика...") model = CatBoostRegressor() model.load_model("bishkek_traffic_model.cbm") print("✅ Модель успешно загружена.") # ============================================================================== # ШАГ 4: ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ # ============================================================================== print("\n🧩 Шаг 4: Сборка функций-компонентов навигатора...") def predict_graph_weights(graph_edges_df, model, timestamp): """Предсказывает время проезда по каждому ребру графа.""" df = graph_edges_df.copy() df['hour'] = timestamp.hour df['minute'] = timestamp.minute df['day_of_week'] = timestamp.weekday() df.rename(columns={'highway': 'highway_cat'}, inplace=True) required_features = ['lanes', 'maxspeed', 'length', 'poi_count', 'highway_cat', 'hour', 'minute', 'day_of_week'] for col in required_features: if col not in df.columns: df[col] = 0 return model.predict(df[required_features]) def find_fastest_route(G, start_node, end_node, weight='travel_time'): """Находит единственный кратчайший маршрут.""" try: route = nx.shortest_path(G, source=start_node, target=end_node, weight=weight) time = nx.shortest_path_length(G, source=start_node, target=end_node, weight=weight) return route, time except nx.NetworkXNoPath: return None, None def get_signal_delay(day_of_week, hour): """Определяет задержку на светофоре согласно правилам.""" is_weekday = 0 <= day_of_week <= 4 is_morning_rush = 8 <= hour <= 10 is_evening_rush = 17 <= hour <= 20 if is_weekday and (is_morning_rush or is_evening_rush): return 35 is_night_time = hour >= 23 or hour <= 7 if is_night_time: return 20 if is_weekday: return 25 else: return 20 # --- ИЗМЕНЕНИЕ ДИЗАЙНА ЗДЕСЬ --- def plot_route_on_map(G, route, start_point, end_point): """Отрисовывает красивый зеленый маршрут с эффектом подсветки.""" m = folium.Map(tiles="OpenStreetMap") if route: points = [(G.nodes[node]['y'], G.nodes[node]['x']) for node in route] # Слой 1: Внешняя подсветка (широкая, полупрозрачная) folium.PolyLine(points, color='#6ee7b7', weight=12, opacity=0.7).add_to(m) # Слой 2: Белый контур для контрастности folium.PolyLine(points, color="white", weight=8, opacity=1).add_to(m) # Слой 3: Основная линия маршрута (насыщенный зеленый) main_line = folium.PolyLine(points, color="#16a34a", weight=5, opacity=1, tooltip="Оптимальный маршрут") main_line.add_to(m) m.fit_bounds(main_line.get_bounds(), padding=(30, 30)) # Стильные маркеры folium.Marker( location=start_point, popup="Точка А (Старт)", icon=folium.Icon(color="green", icon="play-circle", prefix='fa') ).add_to(m) folium.Marker( location=end_point, popup="Точка Б (Финиш)", icon=folium.Icon(color="red", icon="flag-checkered", prefix='fa') ).add_to(m) return m print("✅ Вспомогательные функции готовы.") # ============================================================================== # ШАГ 5: ГЛАВНАЯ ФУНКЦИЯ И ИНТЕРФЕЙС GRADIO # ============================================================================== print("\n🚀 Шаг 5: Настройка и запуск веб-интерфейса Gradio...") geolocator = Nominatim(user_agent="bishkek_navigator_app_v9") geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1) def find_and_plot_route_by_address(start_address, end_address, day_of_week, hour, minute): """Основная функция: принимает адреса, строит маршрут и возвращает карту и информацию.""" if not start_address or not end_address: return None, "### Внимание\nПожалуйста, введите оба адреса." try: location_a = geocode(f"{start_address}, Бишкек") location_b = geocode(f"{end_address}, Бишкек") if not location_a or not location_b: return None, "### Ошибка\nНе удалось найти один или оба адреса. Попробуйте уточнить запрос." start_point = (location_a.latitude, location_a.longitude) end_point = (location_b.latitude, location_b.longitude) start_node = ox.nearest_nodes(G, Y=start_point[0], X=start_point[1]) end_node = ox.nearest_nodes(G, Y=end_point[0], X=end_point[1]) current_hour = int(hour) selected_time = datetime.datetime(2023, 1, 2 + day_of_week, current_hour, int(minute)) travel_times = predict_graph_weights(gdf_edges, model, selected_time) nx.set_edge_attributes(G, values=pd.Series(travel_times, index=gdf_edges.index).to_dict(), name='travel_time') route, travel_time = find_fastest_route(G, start_node, end_node, weight='travel_time') if route is None: return None, "### Ошибка\nНе удалось построить маршрут. Возможно, точки находятся в несвязанных частях города." SIGNAL_DELAY_SECONDS = get_signal_delay(day_of_week, current_hour) signals_on_route = sum(1 for node in route if node in signal_nodes_set) total_time_sec = travel_time + signals_on_route * SIGNAL_DELAY_SECONDS total_time_min = total_time_sec / 60 distance_km = sum(G[u][v][0]['length'] for u, v in zip(route[:-1], route[1:])) / 1000 final_map = plot_route_on_map(G, route, start_point, end_point) # --- ИЗМЕНЕНИЕ ЦВЕТА ТЕКСТА ЗДЕСЬ --- output_md = f""" ### Маршрут построен! - **Время в пути:** **~{total_time_min:.1f} мин.** - **Расстояние:** **{distance_km:.2f} км** - **Светофоров на пути:** **{signals_on_route}**
(задержка на светофор: {SIGNAL_DELAY_SECONDS} сек.)
""" return final_map._repr_html_(), output_md except Exception as e: return None, f"### Произошла внутренняя ошибка:\n`{e}`" # CSS для новой сине-голубой темы css = """ body { font-family: 'Inter', sans-serif; } .gradio-container { max-width: 100% !important; } .gr-button-primary { background: linear-gradient(to right, #60a5fa, #3b82f6); color: white; font-weight: bold; border: none; transition: all 0.3s ease; box-shadow: 0 4px 14px 0 rgba(59, 130, 246, 0.39); } .gr-button-primary:hover { background: linear-gradient(to right, #3b82f6, #2563eb); box-shadow: 0 6px 20px 0 rgba(59, 130, 246, 0.45); transform: translateY(-1px); } #info-box { background-color: #f9fafb; padding: 1.5rem !important; border-radius: 0.75rem; border: 1px solid #e5e7eb;} """ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="blue", font=gr.themes.GoogleFont("Inter")), css=css) as demo: gr.Markdown( """Постройте оптимальный маршрут с учётом прогноза пробок