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

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +42 -47
src/streamlit_app.py CHANGED
@@ -1,6 +1,7 @@
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"
@@ -17,35 +18,38 @@ 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)
@@ -53,7 +57,7 @@ def build_graph(gdf_nodes, gdf_edges, gdf_poi):
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}"
@@ -64,33 +68,30 @@ def build_graph(gdf_nodes, gdf_edges, gdf_poi):
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)
@@ -99,28 +100,22 @@ if st.sidebar.button("πŸ” Cari Rute"):
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.")
 
1
  import os
2
+ from pathlib import Path
3
 
4
+ # ── SUPPRESS telemetry & permission errors ─────────────────────────────────
5
  os.environ["HOME"] = "/tmp"
6
  os.environ["XDG_CONFIG_HOME"] = "/tmp"
7
  os.environ["XDG_CACHE_HOME"] = "/tmp"
 
18
  import warnings
19
  warnings.filterwarnings("ignore")
20
 
21
+ # ── PATH SETUP ───────────────────────────────────────────────────────────────
22
+ # project root is two levels up from this file (src/)
23
+ ROOT_DIR = Path(__file__).resolve().parent.parent
24
+ DATA_DIR = ROOT_DIR / "data"
25
+
26
+ # ── STREAMLIT CONFIG ─────────────────────────────────────────────────────────
27
  st.set_page_config(
28
  page_title="MODA Multimodal Routing",
29
  layout="wide",
30
  initial_sidebar_state="expanded"
31
  )
32
 
33
+ # ── LOAD DATA ─────────────────────────────────────────────────────────────────
34
  @st.cache_data
35
  def load_data():
36
+ """
37
+ Load POI and road network CSVs from data/ directory under project root.
38
+ """
39
+ # load and lowercase columns
40
+ gdf_poi = pd.read_csv(DATA_DIR / "poi_places.csv").rename(columns=str.lower)
41
+ gdf_nodes = pd.read_csv(DATA_DIR / "road_nodes.csv").rename(columns=str.lower)
42
+ gdf_edges = pd.read_csv(DATA_DIR / "road_edges.csv").rename(columns=str.lower)
43
+ return gdf_poi, gdf_nodes, gdf_edges
 
 
44
 
45
+ # ── BUILD GRAPH ─────────────────────────────────────────────────────────────
46
  @st.cache_data
47
  def build_graph(gdf_nodes, gdf_edges, gdf_poi):
48
  G = nx.Graph()
49
  # add road nodes
50
  for _, r in gdf_nodes.iterrows():
51
+ G.add_node(r.osmid, x=r.x, y=r.y)
52
+ # add road edges
53
  for _, r in gdf_edges.iterrows():
54
  if pd.notna(r.length):
55
  G.add_edge(r.u, r.v, length=r.length)
 
57
  for idx, r in gdf_poi.iterrows():
58
  nid = f"poi_{idx}"
59
  G.add_node(nid, x=r.longitude, y=r.latitude, name=r.name)
60
+ # connect POI to nearest road node
61
  road_list = [(n, d['y'], d['x']) for n, d in G.nodes(data=True) if isinstance(n, (int, float))]
62
  for idx, r in gdf_poi.iterrows():
63
  nid = f"poi_{idx}"
 
68
  G.add_edge(nearest, nid, length=dist)
69
  return G
70
 
71
+ # ── ROUTING HELPERS ─────────────────────────────────────────────────────────
72
  def get_poi_id(name, gdf_poi):
73
+ match = gdf_poi[gdf_poi.name.str.lower().str.strip() == name.lower().strip()]
74
+ return f"poi_{match.index[0]}" if not match.empty else None
75
 
 
76
  def compute_route(G, start_id, end_id, speed_kmh):
77
  path = nx.shortest_path(G, start_id, end_id, weight='length')
78
+ d_m = nx.shortest_path_length(G, start_id, end_id, weight='length')
79
+ km = d_m/1000
80
+ t_min = km/speed_kmh*60
81
+ return path, km, t_min
82
 
83
+ # ── STREAMLIT APP ────────────────────────────────────────────────────────────
84
  # load data
85
+ gdf_poi, gdf_nodes, gdf_edges = load_data()
 
 
 
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
+ # main
95
  if st.sidebar.button("πŸ” Cari Rute"):
96
  G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
97
  s_id = get_poi_id(start, gdf_poi)
 
100
  st.sidebar.error("Pilih lokasi yang valid.")
101
  else:
102
  # Drive
103
+ path_d, km_d, t_d = compute_route(G, s_id, e_id, speed_kmh=30)
104
+ cost = int(km_d*3000)
105
  st.subheader("πŸš— Drive Mode")
106
+ st.write(f"**Jarak:** {km_d:.2f} km β€’ **Waktu:** {t_d:.1f} menit β€’ **Biaya:** {cost:,} IDR")
 
 
107
  m1 = folium.Map(location=[gdf_poi.latitude.mean(), gdf_poi.longitude.mean()], zoom_start=13)
108
+ coords1 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_d]
109
+ folium.PolyLine(coords1, color='blue', weight=4).add_to(m1)
110
+ st_folium(m1, width=700, height=400)
111
  # Bus
112
+ path_b, km_b, t_b = compute_route(G, s_id, e_id, speed_kmh=25)
113
+ arrival = (datetime.combine(datetime.today(), time_input) + timedelta(minutes=t_b)).time()
114
  st.subheader("🚌 Bus Mode")
115
+ st.write(f"**Jarak:** {km_b:.2f} km β€’ **Waktu:** {t_b:.1f} menit β€’ **Tiba:** {arrival.strftime('%H:%M')} β€’ **Biaya:** 3,500 IDR")
 
 
 
116
  m2 = folium.Map(location=[gdf_poi.latitude.mean(), gdf_poi.longitude.mean()], zoom_start=13)
117
  coords2 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_b]
118
  folium.PolyLine(coords2, color='green', weight=4).add_to(m2)
119
+ st_folium(m2, width=700, height=400)
 
120
  else:
121
  st.info("Pilih rute dan klik **Cari Rute** di sidebar.")