rvouyaa commited on
Commit
a8d1596
Β·
verified Β·
1 Parent(s): 30b8149

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +105 -114
src/streamlit_app.py CHANGED
@@ -1,144 +1,135 @@
1
  import streamlit as st
2
  import pandas as pd
 
3
  import osmnx as ox
4
  import networkx as nx
5
  import geopandas as gpd
6
  from shapely.geometry import LineString
7
  from geopy.distance import geodesic
8
  from datetime import datetime, timedelta
9
- import matplotlib.pyplot as plt
10
  from streamlit_folium import st_folium
11
  import folium
12
  import warnings
13
  warnings.filterwarnings("ignore")
14
 
15
- # Load Data
16
- data = pd.read_csv("D:\uni-sem2\tegraf\FP\poi_places.csv")
17
- df_nodes = pd.read_csv("D:\uni-sem2\tegraf\FP\road_nodes.csv")
18
- df_edges = pd.read_csv("D:\uni-sem2\tegraf\FP\road_edges.csv")
19
 
20
- df_nodes['geometry'] = gpd.GeoSeries.from_wkt(df_nodes['geometry'])
21
- df_edges['geometry'] = gpd.GeoSeries.from_wkt(df_edges['geometry'])
 
 
 
 
22
 
23
- # Convert to GeoDataFrame
24
- gdf_nodes = gpd.GeoDataFrame(df_nodes, geometry='geometry', crs="EPSG:4326")
25
- gdf_edges = gpd.GeoDataFrame(df_edges, geometry='geometry', crs="EPSG:4326")
 
 
26
 
27
- gdf_bld = gpd.GeoDataFrame(
28
- data,
29
- geometry=gpd.points_from_xy(data.longitude, data.latitude),
30
- crs="EPSG:4326"
31
- )
 
 
 
 
 
 
 
 
 
32
 
33
- # Build Graph G
34
- G = nx.Graph()
35
- for idx, row in gdf_nodes.iterrows():
36
- G.add_node(row['osmid'], x=row.geometry.x, y=row.geometry.y)
37
 
38
- for idx, row in gdf_edges.iterrows():
39
- if pd.notna(row['length']):
40
- G.add_edge(row['u'], row['v'], length=row['length'])
41
 
42
- for idx, row in gdf_bld.iterrows():
43
- nid = f"bldg_{idx}"
44
- G.add_node(nid, x=row.geometry.x, y=row.geometry.y, name=row['name'])
45
 
46
- road_nodes_list = [(n, data['y'], data['x']) for n, data in G.nodes(data=True) if isinstance(n, (int, float))]
 
 
 
 
 
 
47
 
48
- for idx, row in gdf_bld.iterrows():
49
- bnode = f"bldg_{idx}"
50
- b_lat, b_lon = row.latitude, row.longitude
51
- nearest = min(road_nodes_list, key=lambda item: geodesic((b_lat, b_lon), (item[1], item[2])).meters)[0]
52
- dist = geodesic((b_lat, b_lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
53
- G.add_edge(bnode, nearest, length=dist)
54
- G.add_edge(nearest, bnode, length=dist)
55
 
56
- def get_building_node_id_by_name(name):
57
- match = gdf_bld[gdf_bld['name'].str.lower().str.strip() == name.lower().strip()]
58
  if not match.empty:
59
  idx = match.index[0]
60
- return f"bldg_{idx}"
61
  else:
62
- st.error(f"Building name '{name}' not found.")
63
  return None
64
 
65
- def plot_path(path, title):
66
- path_edges = list(zip(path, path[1:]))
67
- lines = []
68
- for u, v in path_edges:
69
- x1, y1 = G.nodes[u]['x'], G.nodes[u]['y']
70
- x2, y2 = G.nodes[v]['x'], G.nodes[v]['y']
71
- lines.append(LineString([(x1, y1), (x2, y2)]))
72
-
73
- gdf_path = gpd.GeoDataFrame(geometry=lines, crs="EPSG:4326")
74
- ax = gdf_edges.plot(figsize=(10,10), linewidth=0.5, edgecolor='gray')
75
- gdf_bld.plot(ax=ax, color='blue', markersize=30, label='POI')
76
- gdf_path.plot(ax=ax, linewidth=3, color='red', label='Shortest Path')
77
- plt.legend()
78
- plt.title(title)
79
- st.pyplot(plt)
80
-
81
- def Gojek_mode(start_name, end_name):
82
- src = get_building_node_id_by_name(start_name)
83
- dst = get_building_node_id_by_name(end_name)
84
- if src is None or dst is None:
85
- return
86
- path = nx.dijkstra_path(G, source=src, target=dst, weight='length')
87
- dist_m = nx.shortest_path_length(G, source=src, target=dst, weight='length')
88
  dist_km = dist_m / 1000
 
89
  cost = int(dist_km * 3000)
90
- time_min = (dist_km / 30) * 60
91
- st.subheader("πŸš— Drive Mode")
92
- st.markdown(f"**Distance:** {dist_km:.2f} km")
93
- st.markdown(f"**Estimated Time:** {time_min:.1f} minutes")
94
- st.markdown(f"**Estimated Cost:** {cost} IDR")
95
- plot_path(path, f"Gojek Route: {start_name} β†’ {end_name}")
96
-
97
- def busMode(start_name, end_name, time_str):
98
- bus_stops = gdf_bld[gdf_bld['tag'] == 'bus_stop']
99
- def get_nearest_bus_stop(coord):
100
- nearest = None
101
- min_dist = float('inf')
102
- for idx, row in bus_stops.iterrows():
103
- dist = geodesic(coord, (row['latitude'], row['longitude'])).meters
104
- if dist < min_dist:
105
- min_dist = dist
106
- nearest = f"bldg_{row.name}"
107
- return nearest
108
-
109
- src_coord = gdf_bld[gdf_bld['name'] == start_name][['latitude','longitude']].values
110
- dst_coord = gdf_bld[gdf_bld['name'] == end_name][['latitude','longitude']].values
111
-
112
- if not len(src_coord) or not len(dst_coord):
113
- st.error("Could not find coordinates.")
114
- return
115
-
116
- node_start = get_nearest_bus_stop(tuple(src_coord[0]))
117
- node_end = get_nearest_bus_stop(tuple(dst_coord[0]))
118
-
119
- if node_start is None or node_end is None:
120
- st.error("Nearest bus stop not found.")
121
- return
122
-
123
- path = nx.shortest_path(G, source=node_start, target=node_end, weight='length')
124
- dist_m = nx.shortest_path_length(G, source=node_start, target=node_end, weight='length')
125
  dist_km = dist_m / 1000
126
- time_min = (dist_km / 25) * 60
127
- arrival = datetime.strptime(time_str, "%H:%M") + timedelta(minutes=time_min)
128
-
129
- st.subheader("🚌 Bus Mode")
130
- st.markdown(f"**Distance:** {dist_km:.2f} km")
131
- st.markdown(f"**Travel Time:** {time_min:.1f} minutes")
132
- st.markdown(f"**Arrival Time:** {arrival.strftime('%H:%M')}")
133
- st.markdown(f"**Estimated Cost:** 3500 IDR")
134
- plot_path(path, f"Bus Route: {start_name} β†’ {end_name}")
135
-
136
- # --- Streamlit UI ---
137
- st.title("πŸ—ΊοΈ Multimodal Shortest Path Finder - Jakarta")
138
- start = st.text_input("Start Location", "Universitas Indonesia")
139
- end = st.text_input("End Location", "Stasiun Gambir")
140
- time_now = st.time_input("Departure Time")
141
-
142
- if st.button("Find Routes"):
143
- Gojek_mode(start, end)
144
- busMode(start, end, time_now.strftime("%H:%M"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import pandas as pd
3
+ import os
4
  import osmnx as ox
5
  import networkx as nx
6
  import geopandas as gpd
7
  from shapely.geometry import LineString
8
  from geopy.distance import geodesic
9
  from datetime import datetime, timedelta
 
10
  from streamlit_folium import st_folium
11
  import folium
12
  import warnings
13
  warnings.filterwarnings("ignore")
14
 
15
+ # Atasi PermissionError di Hugging Face
16
+ os.environ["MPLCONFIGDIR"] = "/tmp"
17
+ os.environ["STREAMLIT_STATIC_DIR"] = "/tmp"
 
18
 
19
+ st.set_page_config(
20
+ page_title="Multimodal Rute Jakarta",
21
+ page_icon="πŸ›£οΈ",
22
+ layout="wide",
23
+ initial_sidebar_state="expanded"
24
+ )
25
 
26
+ @st.cache_data
27
+ def load_data():
28
+ data = pd.read_csv("data/poi_places.csv")
29
+ df_nodes = pd.read_csv("data/road_nodes.csv")
30
+ df_edges = pd.read_csv("data/road_edges.csv")
31
 
32
+ df_nodes['geometry'] = gpd.GeoSeries.from_wkt(df_nodes['geometry'])
33
+ df_edges['geometry'] = gpd.GeoSeries.from_wkt(df_edges['geometry'])
34
+
35
+ gdf_nodes = gpd.GeoDataFrame(df_nodes, geometry='geometry', crs="EPSG:4326")
36
+ gdf_edges = gpd.GeoDataFrame(df_edges, geometry='geometry', crs="EPSG:4326")
37
+ gdf_poi = gpd.GeoDataFrame(data, geometry=gpd.points_from_xy(data.longitude, data.latitude), crs="EPSG:4326")
38
+
39
+ return gdf_nodes, gdf_edges, gdf_poi
40
+
41
+ @st.cache_data
42
+ def build_graph(gdf_nodes, gdf_edges, gdf_poi):
43
+ G = nx.Graph()
44
+ for _, row in gdf_nodes.iterrows():
45
+ G.add_node(row['osmid'], x=row.geometry.x, y=row.geometry.y)
46
 
47
+ for _, row in gdf_edges.iterrows():
48
+ if pd.notna(row['length']):
49
+ G.add_edge(row['u'], row['v'], length=row['length'])
 
50
 
51
+ for idx, row in gdf_poi.iterrows():
52
+ nid = f"poi_{idx}"
53
+ G.add_node(nid, x=row.geometry.x, y=row.geometry.y, name=row['name'])
54
 
55
+ road_nodes_list = [(n, data['y'], data['x']) for n, data in G.nodes(data=True) if isinstance(n, (int, float))]
 
 
56
 
57
+ for idx, row in gdf_poi.iterrows():
58
+ bnode = f"poi_{idx}"
59
+ b_lat, b_lon = row.latitude, row.longitude
60
+ nearest = min(road_nodes_list, key=lambda item: geodesic((b_lat, b_lon), (item[1], item[2])).meters)[0]
61
+ dist = geodesic((b_lat, b_lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
62
+ G.add_edge(bnode, nearest, length=dist)
63
+ G.add_edge(nearest, bnode, length=dist)
64
 
65
+ return G
 
 
 
 
 
 
66
 
67
+ def get_node_id_by_name(name, gdf):
68
+ match = gdf[gdf['name'].str.lower().str.strip() == name.lower().strip()]
69
  if not match.empty:
70
  idx = match.index[0]
71
+ return f"poi_{idx}"
72
  else:
 
73
  return None
74
 
75
+ def plot_folium_path(G, path, start_name, end_name):
76
+ m = folium.Map(location=[-6.18, 106.83], zoom_start=14)
77
+ coords = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path if 'x' in G.nodes[n] and 'y' in G.nodes[n]]
78
+ folium.PolyLine(coords, color='blue', weight=5, popup='Rute').add_to(m)
79
+
80
+ folium.Marker(coords[0], tooltip='Start', icon=folium.Icon(color='green')).add_to(m)
81
+ folium.Marker(coords[-1], tooltip='Destination', icon=folium.Icon(color='red')).add_to(m)
82
+
83
+ return m
84
+
85
+ def compute_drive(G, start_id, end_id):
86
+ path = nx.shortest_path(G, start_id, end_id, weight='length')
87
+ dist_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
 
 
 
 
 
 
 
 
 
 
88
  dist_km = dist_m / 1000
89
+ est_time = (dist_km / 30) * 60
90
  cost = int(dist_km * 3000)
91
+ return path, dist_km, est_time, cost
92
+
93
+ def compute_bus(G, start_id, end_id, time_now):
94
+ path = nx.shortest_path(G, start_id, end_id, weight='length')
95
+ dist_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  dist_km = dist_m / 1000
97
+ est_time = (dist_km / 25) * 60
98
+ arrival = datetime.strptime(time_now, "%H:%M") + timedelta(minutes=est_time)
99
+ return path, dist_km, est_time, arrival.strftime("%H:%M")
100
+
101
+ # ==== UI ====
102
+ st.title("🚏 Multimodal Shortest Path Jakarta")
103
+ st.markdown("Bandingkan rute tercepat antara **Drive** dan **Bus** dari titik-titik penting di Jakarta Pusat.")
104
+
105
+ with st.sidebar:
106
+ st.header("πŸ“ Input Rute")
107
+ gdf_nodes, gdf_edges, gdf_poi = load_data()
108
+ name_list = sorted(gdf_poi['name'].dropna().unique().tolist())
109
+ start_name = st.selectbox("Pilih Lokasi Awal:", name_list)
110
+ end_name = st.selectbox("Pilih Lokasi Tujuan:", name_list, index=1)
111
+ time_now = st.time_input("Jam Keberangkatan", value=datetime.now().time())
112
+
113
+ if st.button("πŸ” Cari Rute"):
114
+ G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
115
+
116
+ src = get_node_id_by_name(start_name, gdf_poi)
117
+ dst = get_node_id_by_name(end_name, gdf_poi)
118
+
119
+ if src is None or dst is None:
120
+ st.error("Lokasi tidak ditemukan dalam daftar POI.")
121
+ else:
122
+ st.subheader("πŸš— Drive Mode")
123
+ drive_path, drive_dist, drive_time, drive_cost = compute_drive(G, src, dst)
124
+ st.write(f"**Jarak:** {drive_dist:.2f} km")
125
+ st.write(f"**Estimasi Waktu:** {drive_time:.1f} menit")
126
+ st.write(f"**Biaya:** {drive_cost:,} IDR")
127
+ st_folium(plot_folium_path(G, drive_path, start_name, end_name), width=700)
128
+
129
+ st.subheader("🚌 Bus Mode")
130
+ bus_path, bus_dist, bus_time, bus_arrival = compute_bus(G, src, dst, time_now.strftime("%H:%M"))
131
+ st.write(f"**Jarak:** {bus_dist:.2f} km")
132
+ st.write(f"**Estimasi Waktu:** {bus_time:.1f} menit")
133
+ st.write(f"**Waktu Tiba:** {bus_arrival}")
134
+ st.write("**Biaya:** 3.500 IDR")
135
+ st_folium(plot_folium_path(G, bus_path, start_name, end_name), width=700)