Spaces:
Build error
Build error
| import streamlit as st | |
| import osmnx as ox | |
| import networkx as nx | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| import heapq | |
| from math import asin, radians, cos, sin, sqrt | |
| # Plotting | |
| DEFAULT_WEIGHT_KG = 70 | |
| ELEVATION_SCALE = 5 | |
| # --- Location list --- | |
| locations = [ | |
| {'name': 'Grha Sabha Pramana', 'lat': -7.770071488953822, 'lon': 110.3783984379329, 'elevation': 142}, | |
| {'name': 'Boulevard UGM', 'lat': -7.775446818599403, 'lon': 110.37643708634688, 'elevation': 133}, | |
| {'name': 'Fakultas Ilmu Sosial dan Ilmu Politik', 'lat': -7.769082851574989, 'lon': 110.38088752769059, 'elevation': 145}, | |
| {'name': 'Fakultas Teknik', 'lat': -7.765067267048453, 'lon': 110.37252242878728, 'elevation': 139}, | |
| {'name': 'Perpustakaan Pusat', 'lat': -7.769132738499928, 'lon': 110.37819025762326, 'elevation': 142}, | |
| {'name': 'Fakultas Farmasi', 'lat': -7.768920115853235, 'lon': 110.37629125348137, 'elevation': 143}, | |
| {'name': 'Fakultas Kedokteran', 'lat': -7.769241968897335, 'lon': 110.37410241529511, 'elevation': 138}, | |
| {'name': 'Fakultas Hukum', 'lat': -7.770083777982755, 'lon': 110.38117766903606, 'elevation': 143}, | |
| {'name': 'Fakultas Ekonomika dan Bisnis', 'lat': -7.770126237227525, 'lon': 110.37903152864811, 'elevation': 142}, | |
| {'name': 'Fakultas Ilmu Budaya', 'lat': -7.771190276512944, 'lon': 110.37876576685031, 'elevation': 139}, | |
| {'name': 'Fakultas Biologi', 'lat': -7.76442822947009, 'lon': 110.37698323450962, 'elevation': 148}, | |
| {'name': 'Fakultas Kehutanan', 'lat': -7.766739271152903, 'lon': 110.38109875565928, 'elevation': 149}, | |
| {'name': 'Fakultas Peternakan', 'lat': -7.766930486720981, 'lon': 110.38542241877909, 'elevation': 144}, | |
| {'name': 'Fakultas Pertanian', 'lat': -7.767908235085672, 'lon': 110.38100213548348, 'elevation': 148}, | |
| {'name': 'Fakultas Geografi', 'lat': -7.765983577235556, 'lon': 110.37794577655771, 'elevation': 150}, | |
| {'name': 'Fakultas MIPA', 'lat': -7.766770213071046, 'lon': 110.37617551844697, 'elevation': 142}, | |
| {'name': 'Fakultas Psikologi', 'lat': -7.7721010490703355, 'lon': 110.38076316079525, 'elevation': 138}, | |
| {'name': 'Fakultas Filsafat', 'lat': -7.771194580135096, 'lon': 110.38124107602673, 'elevation': 140}, | |
| {'name': 'Rektorat UGM', 'lat': -7.767550457927047, 'lon': 110.37912599375315, 'elevation': 155}, | |
| {'name': 'Masjid Kampus UGM', 'lat': -7.773202436052038, 'lon': 110.38040009798743, 'elevation': 140}, | |
| {'name': 'RSUP Dr. Sardjito', 'lat': -7.76857375077125, 'lon': 110.37380676773756, 'elevation': 138} | |
| ] | |
| # Dynamically generate key_locations for quick lookup | |
| key_locations = {loc["name"]: (loc["lat"], loc["lon"], loc["elevation"]) for loc in locations} | |
| def haversine(lat1, lon1, lat2, lon2): | |
| dlon = radians(lon2 - lon1) | |
| dlat = radians(lat2 - lat1) | |
| a = sin(dlat/2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon/2)**2 | |
| c = 2 * asin(sqrt(a)) | |
| return 6371 * c # km | |
| def elevation_difference(u, v): | |
| # Get the nearest locations for nodes u and v | |
| u_coords = (G.nodes[u]['y'], G.nodes[u]['x']) # lat, lon | |
| v_coords = (G.nodes[v]['y'], G.nodes[v]['x']) # lat, lon | |
| # Find the nearest locations from the locations list | |
| u_location = min(locations, key=lambda loc: haversine(u_coords[0], u_coords[1], loc['lat'], loc['lon'])) | |
| v_location = min(locations, key=lambda loc: haversine(v_coords[0], v_coords[1], loc['lat'], loc['lon'])) | |
| # Get the elevation values from the nearest locations | |
| elev_u = u_location['elevation'] | |
| elev_v = v_location['elevation'] | |
| # Return the absolute elevation difference | |
| return abs(elev_v - elev_u) | |
| def heuristic(u, v): | |
| u_lat, u_lon = G.nodes[u]['y'], G.nodes[u]['x'] | |
| v_lat, v_lon = G.nodes[v]['y'], G.nodes[v]['x'] | |
| return haversine(u_lat, u_lon, v_lat, v_lon) + (ELEVATION_SCALE * elevation_difference(u, v)) | |
| def astar(graph, start, goal): | |
| open_set = [] | |
| heapq.heappush(open_set, (0, start)) | |
| came_from = {} | |
| g_score = {node: float('inf') for node in graph.nodes} | |
| g_score[start] = 0 | |
| f_score = {node: float('inf') for node in graph.nodes} | |
| f_score[start] = heuristic(start, goal) | |
| while open_set: | |
| _, current = heapq.heappop(open_set) | |
| if current == goal: | |
| path = [] | |
| while current in came_from: | |
| path.append(current) | |
| current = came_from[current] | |
| path.append(start) | |
| return path[::-1] | |
| for neighbor in graph.neighbors(current): | |
| tentative_g = g_score[current] + graph[current][neighbor][0]['length'] | |
| if tentative_g < g_score[neighbor]: | |
| came_from[neighbor] = current | |
| g_score[neighbor] = tentative_g | |
| f_score[neighbor] = tentative_g + heuristic(neighbor, goal) | |
| heapq.heappush(open_set, (f_score[neighbor], neighbor)) | |
| raise nx.NetworkXNoPath("No path found") | |
| def get_coordinates(locations, target_name): | |
| for loc in locations: | |
| if loc["name"].lower() == target_name.lower(): | |
| return (loc["lon"], loc["lat"]) # Return in (lon, lat) format | |
| return None | |
| def adjust_map_zoom_and_center(lats, lons, real_distance, total_distance): | |
| # Calculate the center of the route for better zoom | |
| center_lat = sum(lats) / len(lats) | |
| center_lon = sum(lons) / len(lons) | |
| # Adjust zoom level based on the route length | |
| route_length_km = real_distance / 1000 if real_distance else total_distance | |
| zoom_level = max(10, min(16, 16 - int(route_length_km / 2))) | |
| return center_lat, center_lon, zoom_level | |
| # Streamlit App | |
| st.set_page_config(page_title="UGM Running Route", layout="wide") | |
| st.title("๐ถโโ๏ธ Running Route Through UGM") | |
| st.markdown("---") | |
| # Layout: Input, Map, and Output | |
| col1, col2, col3 = st.columns([1, 2, 1]) | |
| # Input Section | |
| with col1: | |
| st.header("๐ Input") | |
| st.markdown("Select your starting and ending points for the route.") | |
| with st.form("route_form"): | |
| st.write("### Route Options") | |
| location_names = [loc["name"] for loc in locations] | |
| start_point = st.selectbox("Start Point", location_names) | |
| end_point = st.selectbox("End Point", location_names) | |
| submitted = st.form_submit_button('๐ Submit Route') | |
| start_loc = next(loc for loc in locations if loc['name'] == start_point) | |
| end_loc = next(loc for loc in locations if loc['name'] == end_point) | |
| center_lat = (start_loc['lat'] + end_loc['lat']) / 2 | |
| center_lon = (start_loc['lon'] + end_loc['lon']) / 2 | |
| G = ox.graph_from_point((center_lat, center_lon), dist=1000, network_type='walk') | |
| start_node = ox.distance.nearest_nodes(G, start_loc['lon'], start_loc['lat']) | |
| end_node = ox.distance.nearest_nodes(G, end_loc['lon'], end_loc['lat']) | |
| #calculations | |
| route = astar(G, start_node, end_node) | |
| route_coords = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in route] # lat, lon | |
| real_distance = sum(G[u][v][0]['length'] for u, v in zip(route[:-1], route[1:])) | |
| elevation_gain = sum(elevation_difference(u, v) for u, v in zip(route[:-1], route[1:])) | |
| if elevation_gain < 5: | |
| difficulty = "Easy" | |
| elif elevation_gain < 15: | |
| difficulty = "Moderate" | |
| else: | |
| difficulty = "Difficult" | |
| calories_burned = {} | |
| for speed in [4, 6, 8]: | |
| time_h = real_distance / 1000 / speed | |
| met = 3.5 if speed == 4 else 7 if speed == 6 else 10 | |
| cal = met * DEFAULT_WEIGHT_KG * time_h | |
| calories_burned[f"{speed} km/h"] = round(cal, 2) | |
| lats, lons = zip(*route_coords) | |
| fig = go.Figure() | |
| for loc in locations: | |
| fig.add_scattermapbox( | |
| lat=[loc['lat']], | |
| lon=[loc['lon']], | |
| mode='markers+text', | |
| marker=dict(size=8, color='red'), | |
| text=[loc['name']], | |
| textposition="top center", | |
| name=loc['name'], | |
| showlegend=False, | |
| hoverinfo='text' | |
| ) | |
| # Route line | |
| fig.add_trace(go.Scattermapbox( | |
| lat=lats, | |
| lon=lons, | |
| mode='lines+markers', | |
| line=dict(width=4, color='blue'), | |
| marker=dict(size=6, color='red'), | |
| name='A* Route' | |
| )) | |
| # Start point | |
| fig.add_trace(go.Scattermapbox( | |
| lat=[start_loc['lat']], | |
| lon=[start_loc['lon']], | |
| mode='markers+text', | |
| marker=dict(size=12, color='green'), | |
| text=[start_point], | |
| textposition="top center", | |
| name='Start' | |
| )) | |
| # End point | |
| fig.add_trace(go.Scattermapbox( | |
| lat=[end_loc['lat']], | |
| lon=[end_loc['lon']], | |
| mode='markers+text', | |
| marker=dict(size=12, color='green'), | |
| text=[end_point], | |
| textposition="top center", | |
| name='End' | |
| )) | |
| # Map layout | |
| fig.update_layout( | |
| mapbox_style="open-street-map", | |
| mapbox_zoom=16, | |
| mapbox_center={"lat": center_lat, "lon": center_lon}, | |
| margin={"r":0,"t":0,"l":0,"b":0}, | |
| title="A* Route Between UGM Locations" | |
| ) | |
| # Use the new method to adjust map zoom and center | |
| # center_lat, center_lon, zoom_level = adjust_map_zoom_and_center(lats, lons, real_distance, total_distance) | |
| fig.update_layout( | |
| mapbox=dict( | |
| center=dict(lat=center_lat, lon=center_lon), | |
| zoom=14 | |
| ) | |
| ) | |
| fig.update_layout( | |
| mapbox_style="open-street-map", | |
| margin={"r":0, "t":0, "l":0, "b":0} | |
| ) | |
| # Map Section | |
| with col2: | |
| st.header("๐บ๏ธ Map") | |
| if submitted: | |
| st.plotly_chart(fig, use_container_width=True) | |
| else: | |
| st.info("Submit the form to see the map.") | |
| # Output Section | |
| with col3: | |
| st.header("๐ Output") | |
| if submitted: | |
| st.success("### Route Summary") | |
| st.metric(label="Real Distance (km)", value="{:.2f}".format(real_distance/1000) if real_distance else "N/A") | |
| st.metric(label="Elevation Gain (m)", value=round(elevation_gain, 2)) | |
| st.metric(label="Difficulty", value=difficulty) | |
| st.write("### Calories Burned") | |
| for speed, calories in calories_burned.items(): | |
| st.write(f"- **{speed}:** {calories} kcal") | |
| else: | |
| st.info("Submit the form to see the output.") | |
| st.markdown("---") | |
| st.caption("Developed with โค๏ธ for UGM runners.") |