rvouyaa commited on
Commit
bfe6cc2
Β·
verified Β·
1 Parent(s): 6235d53

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +92 -116
src/streamlit_app.py CHANGED
@@ -1,150 +1,126 @@
1
  import os
2
 
3
- # βœ… FIX HuggingFace PermissionError
4
- os.environ["STREAMLIT_HOME"] = "/tmp"
5
  os.environ["XDG_CONFIG_HOME"] = "/tmp"
6
  os.environ["XDG_CACHE_HOME"] = "/tmp"
 
 
7
 
8
  import streamlit as st
9
  import pandas as pd
10
- import numpy as np
11
  import networkx as nx
12
  import folium
13
  from streamlit_folium import st_folium
14
  from geopy.distance import geodesic
15
  from datetime import datetime, timedelta
 
 
16
 
17
- # Dapatkan path absolut direktori saat ini (yaitu src/)
18
- BASE_DIR = os.path.dirname(__file__)
19
-
20
- # Konfigurasi halaman Streamlit
21
  st.set_page_config(
22
- page_title="MODA",
23
  layout="wide",
24
  initial_sidebar_state="expanded"
25
  )
26
 
27
- # Fungsi untuk memuat data CSV dengan caching
28
  @st.cache_data
29
- def load_csv_from_local(file_path):
30
- try:
31
- full_path = os.path.join(BASE_DIR, file_path)
32
- df = pd.read_csv(full_path)
33
- df.columns = [col.lower() for col in df.columns]
34
- return df
35
- except Exception as e:
36
- st.error(f"Error memuat data dari {file_path}: {e}")
37
- return pd.DataFrame()
38
-
39
- @st.cache_data
40
- def load_all_data():
41
- file_paths = {
42
- 'buildings': '../data/poi_places.csv',
43
- 'road_nodes': '../data/road_nodes.csv',
44
- 'road_edges': '../data/road_edges.csv'
45
  }
46
- data = {}
47
- for key, path in file_paths.items():
48
- data[key] = load_csv_from_local(path)
49
- return data
50
 
 
51
  @st.cache_data
52
  def build_graph(gdf_nodes, gdf_edges, gdf_poi):
53
  G = nx.Graph()
54
- for _, row in gdf_nodes.iterrows():
55
- G.add_node(row['osmid'], x=row.geometry.x, y=row.geometry.y)
56
-
57
- for _, row in gdf_edges.iterrows():
58
- if pd.notna(row['length']):
59
- G.add_edge(row['u'], row['v'], length=row['length'])
60
-
61
- for idx, row in gdf_poi.iterrows():
 
62
  nid = f"poi_{idx}"
63
- G.add_node(nid, x=row['longitude'], y=row['latitude'], name=row.get('name', f"POI {idx}"))
64
-
65
- road_nodes_list = [(n, data['y'], data['x']) for n, data in G.nodes(data=True) if isinstance(n, (int, float))]
66
-
67
- for idx, row in gdf_poi.iterrows():
68
- bnode = f"poi_{idx}"
69
- b_lat, b_lon = row.latitude, row.longitude
70
- nearest = min(road_nodes_list, key=lambda item: geodesic((b_lat, b_lon), (item[1], item[2])).meters)[0]
71
- dist = geodesic((b_lat, b_lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
72
- G.add_edge(bnode, nearest, length=dist)
73
- G.add_edge(nearest, bnode, length=dist)
74
-
75
  return G
76
 
77
- def get_node_id_by_name(name, gdf):
78
- if 'name' not in gdf.columns:
79
- return None
80
- match = gdf[gdf['name'].str.lower().str.strip() == name.lower().strip()]
81
- return f"poi_{match.index[0]}" if not match.empty else None
82
-
83
- def plot_folium_path(G, path, start_name, end_name):
84
- m = folium.Map(location=[-6.18, 106.83], zoom_start=14)
85
- 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]]
86
- folium.PolyLine(coords, color='blue', weight=5, popup='Rute').add_to(m)
87
- folium.Marker(coords[0], tooltip='Start', icon=folium.Icon(color='green')).add_to(m)
88
- folium.Marker(coords[-1], tooltip='Destination', icon=folium.Icon(color='red')).add_to(m)
89
- return m
90
 
91
- def compute_drive(G, start_id, end_id):
 
92
  path = nx.shortest_path(G, start_id, end_id, weight='length')
93
  dist_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
94
- dist_km = dist_m / 1000
95
- est_time = (dist_km / 30) * 60
96
- cost = int(dist_km * 3000)
97
- return path, dist_km, est_time, cost
98
-
99
- def compute_bus(G, start_id, end_id, time_now):
100
- path = nx.shortest_path(G, start_id, end_id, weight='length')
101
- dist_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
102
- dist_km = dist_m / 1000
103
- est_time = (dist_km / 25) * 60
104
- arrival = datetime.strptime(time_now, "%H:%M") + timedelta(minutes=est_time)
105
- return path, dist_km, est_time, arrival.strftime("%H:%M")
106
-
107
- # ==== UI ====
108
- st.title("🚏 Multimodal Shortest Path Jakarta")
109
- st.markdown("Bandingkan rute tercepat antara **Drive** dan **Bus** dari titik-titik penting di Jakarta Pusat.")
110
-
111
- with st.sidebar:
112
- st.header("πŸ“ Input Rute")
113
- data = load_all_data()
114
- gdf_nodes = data["road_nodes"]
115
- gdf_edges = data["road_edges"]
116
- gdf_poi = data["buildings"]
117
-
118
- st.write("πŸ“‹ Kolom tersedia di gdf_poi:", gdf_poi.columns.tolist())
119
-
120
- if 'name' in gdf_poi.columns:
121
- name_list = sorted(gdf_poi['name'].dropna().unique().tolist())
122
- start_name = st.selectbox("Pilih Lokasi Awal:", name_list)
123
- end_name = st.selectbox("Pilih Lokasi Tujuan:", name_list, index=1)
124
- time_now = st.time_input("Jam Keberangkatan", value=datetime.now().time())
125
- else:
126
- st.error("Kolom 'name' tidak ditemukan di poi_places.csv. Kolom yang tersedia: " + ", ".join(gdf_poi.columns))
127
- st.stop()
128
-
129
- if st.button("πŸ” Cari Rute"):
130
  G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
131
- src = get_node_id_by_name(start_name, gdf_poi)
132
- dst = get_node_id_by_name(end_name, gdf_poi)
133
-
134
- if src is None or dst is None:
135
- st.error("Lokasi tidak ditemukan dalam daftar POI.")
136
  else:
 
 
 
137
  st.subheader("πŸš— Drive Mode")
138
- drive_path, drive_dist, drive_time, drive_cost = compute_drive(G, src, dst)
139
- st.write(f"**Jarak:** {drive_dist:.2f} km")
140
- st.write(f"**Estimasi Waktu:** {drive_time:.1f} menit")
141
- st.write(f"**Biaya:** {drive_cost:,} IDR")
142
- st_folium(plot_folium_path(G, drive_path, start_name, end_name), width=700)
143
-
 
 
 
 
144
  st.subheader("🚌 Bus Mode")
145
- bus_path, bus_dist, bus_time, bus_arrival = compute_bus(G, src, dst, time_now.strftime("%H:%M"))
146
- st.write(f"**Jarak:** {bus_dist:.2f} km")
147
- st.write(f"**Estimasi Waktu:** {bus_time:.1f} menit")
148
- st.write(f"**Waktu Tiba:** {bus_arrival}")
149
- st.write("**Biaya:** 3.500 IDR")
150
- st_folium(plot_folium_path(G, bus_path, start_name, end_name), width=700)
 
 
 
 
 
 
1
  import os
2
 
3
+ # πŸ›‘οΈ SUPPRESS Streamlit telemetry and permission errors
4
+ os.environ["HOME"] = "/tmp"
5
  os.environ["XDG_CONFIG_HOME"] = "/tmp"
6
  os.environ["XDG_CACHE_HOME"] = "/tmp"
7
+ os.environ["STREAMLIT_HOME"] = "/tmp"
8
+ os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false"
9
 
10
  import streamlit as st
11
  import pandas as pd
 
12
  import networkx as nx
13
  import folium
14
  from streamlit_folium import st_folium
15
  from geopy.distance import geodesic
16
  from datetime import datetime, timedelta
17
+ import warnings
18
+ warnings.filterwarnings("ignore")
19
 
20
+ # Page config
 
 
 
21
  st.set_page_config(
22
+ page_title="MODA Multimodal Routing",
23
  layout="wide",
24
  initial_sidebar_state="expanded"
25
  )
26
 
27
+ # ─── Data Loading ───────────────────────────────────────────────────────────
28
  @st.cache_data
29
+ def load_data():
30
+ """Load CSVs from data/ folder in repo root."""
31
+ dfs = {
32
+ "gdf_poi": pd.read_csv("data/poi_places.csv"),
33
+ "gdf_nodes": pd.read_csv("data/road_nodes.csv"),
34
+ "gdf_edges": pd.read_csv("data/road_edges.csv")
 
 
 
 
 
 
 
 
 
 
35
  }
36
+ # normalize column names
37
+ for k, df in dfs.items():
38
+ df.columns = [c.lower() for c in df.columns]
39
+ return dfs
40
 
41
+ # ─── Build Graph ────────────────────────────────────────────────────────────
42
  @st.cache_data
43
  def build_graph(gdf_nodes, gdf_edges, gdf_poi):
44
  G = nx.Graph()
45
+ # add road nodes
46
+ for _, r in gdf_nodes.iterrows():
47
+ G.add_node(r.osmid, x=r.geometry.x, y=r.geometry.y)
48
+ # add edges
49
+ for _, r in gdf_edges.iterrows():
50
+ if pd.notna(r.length):
51
+ G.add_edge(r.u, r.v, length=r.length)
52
+ # add POI nodes
53
+ for idx, r in gdf_poi.iterrows():
54
  nid = f"poi_{idx}"
55
+ G.add_node(nid, x=r.longitude, y=r.latitude, name=r.name)
56
+ # connect POIs to nearest road
57
+ road_list = [(n, d['y'], d['x']) for n, d in G.nodes(data=True) if isinstance(n, (int, float))]
58
+ for idx, r in gdf_poi.iterrows():
59
+ nid = f"poi_{idx}"
60
+ lat, lon = r.latitude, r.longitude
61
+ nearest = min(road_list, key=lambda x: geodesic((lat, lon), (x[1], x[2])).meters)[0]
62
+ dist = geodesic((lat, lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
63
+ G.add_edge(nid, nearest, length=dist)
64
+ G.add_edge(nearest, nid, length=dist)
 
 
65
  return G
66
 
67
+ # ─── Routing Helpers ────────────────────────────────────────────────────────
68
+ def get_poi_id(name, gdf_poi):
69
+ df = gdf_poi[gdf_poi.name.str.lower().str.strip() == name.lower().strip()]
70
+ return f"poi_{df.index[0]}" if not df.empty else None
 
 
 
 
 
 
 
 
 
71
 
72
+ # ─── Compute Paths ──────────────────────────────────────────────────────────
73
+ def compute_route(G, start_id, end_id, speed_kmh):
74
  path = nx.shortest_path(G, start_id, end_id, weight='length')
75
  dist_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
76
+ km = dist_m/1000
77
+ time_min = km / speed_kmh * 60
78
+ return path, km, time_min
79
+
80
+ # ─── Streamlit UI ───────────────────────────────────────────────────────────
81
+ # load data
82
+ dfs = load_data()
83
+ gdf_poi = dfs['gdf_poi']
84
+ gdf_nodes = dfs['gdf_nodes']
85
+ gdf_edges = dfs['gdf_edges']
86
+
87
+ # sidebar inputs
88
+ st.sidebar.header("πŸ“ Input Rute")
89
+ poi_list = gdf_poi['name'].tolist()
90
+ start = st.sidebar.selectbox("Dari:", poi_list)
91
+ end = st.sidebar.selectbox("Ke:", poi_list, index=1)
92
+ time_input = st.sidebar.time_input("Waktu Berangkat", value=datetime.now().time())
93
+
94
+ if st.sidebar.button("πŸ” Cari Rute"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
96
+ s_id = get_poi_id(start, gdf_poi)
97
+ e_id = get_poi_id(end, gdf_poi)
98
+ if not s_id or not e_id:
99
+ st.sidebar.error("Pilih lokasi yang valid.")
 
100
  else:
101
+ # Drive
102
+ path_d, d_km, t_min = compute_route(G, s_id, e_id, speed_kmh=30)
103
+ cost = int(d_km*3000)
104
  st.subheader("πŸš— Drive Mode")
105
+ st.write(f"Jarak: **{d_km:.2f} km**")
106
+ st.write(f"Waktu: **{t_min:.1f} menit**")
107
+ st.write(f"Biaya: **{cost:,} IDR**")
108
+ m1 = folium.Map(location=[gdf_poi.latitude.mean(), gdf_poi.longitude.mean()], zoom_start=13)
109
+ coords = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_d]
110
+ folium.PolyLine(coords, color='blue', weight=4).add_to(m1)
111
+ st_folium(m1, width=700, height=500)
112
+ # Bus
113
+ path_b, b_km, b_min = compute_route(G, s_id, e_id, speed_kmh=25)
114
+ arrival = (datetime.combine(datetime.today(), time_input) + timedelta(minutes=b_min)).time()
115
  st.subheader("🚌 Bus Mode")
116
+ st.write(f"Jarak: **{b_km:.2f} km**")
117
+ st.write(f"Waktu: **{b_min:.1f} menit**")
118
+ st.write(f"Tiba: **{arrival.strftime('%H:%M')}**")
119
+ st.write("Biaya: **3500 IDR**")
120
+ m2 = folium.Map(location=[gdf_poi.latitude.mean(), gdf_poi.longitude.mean()], zoom_start=13)
121
+ coords2 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_b]
122
+ folium.PolyLine(coords2, color='green', weight=4).add_to(m2)
123
+ st_folium(m2, width=700, height=500)
124
+
125
+ else:
126
+ st.info("Pilih rute dan klik **Cari Rute** di sidebar.")