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

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +82 -63
src/streamlit_app.py CHANGED
@@ -1,13 +1,5 @@
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"
8
- os.environ["STREAMLIT_HOME"] = "/tmp"
9
- os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false"
10
-
11
  import streamlit as st
12
  import pandas as pd
13
  import networkx as nx
@@ -18,104 +10,131 @@ from datetime import datetime, timedelta
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)
56
- # add POI nodes
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}"
64
- lat, lon = r.latitude, r.longitude
65
  nearest = min(road_list, key=lambda x: geodesic((lat, lon), (x[1], x[2])).meters)[0]
66
  dist = geodesic((lat, lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
67
  G.add_edge(nid, nearest, length=dist)
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)
98
- e_id = get_poi_id(end, gdf_poi)
99
- if not s_id or not e_id:
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.")
 
1
  import os
2
  from pathlib import Path
 
 
 
 
 
 
 
 
3
  import streamlit as st
4
  import pandas as pd
5
  import networkx as nx
 
10
  import warnings
11
  warnings.filterwarnings("ignore")
12
 
13
+ # ─── ENVIRONMENT FIX FOR HUGGINGFACE SPACES ─────────────────────────────────
14
+ os.environ["HOME"] = "/tmp"
15
+ os.environ["XDG_CONFIG_HOME"] = "/tmp"
16
+ os.environ["XDG_CACHE_HOME"] = "/tmp"
17
+ os.environ["STREAMLIT_HOME"] = "/tmp"
18
 
19
+ # ─── CONFIGURE PAGE ─────────────────────────────────────────────────────────
20
  st.set_page_config(
21
  page_title="MODA Multimodal Routing",
22
  layout="wide",
23
  initial_sidebar_state="expanded"
24
  )
25
 
26
+ # ─── PATH SETUP ──────────────────────────────────────────────────────────────
27
+ BASE_DIR = Path(__file__).resolve().parent
28
+ DATA_DIR = BASE_DIR.parent / "data"
29
+
30
+ # ─── DATA LOADING FUNCTIONS ───────────────────────────────────────────────────
31
  @st.cache_data
32
+ def load_csv_from_local(file_path):
33
+ """Memuat CSV dengan caching. file_path relatif terhadap src/"""
34
+ full_path = DATA_DIR / file_path
35
+ if not full_path.exists():
36
+ st.error(f"File tidak ditemukan: {full_path}")
37
+ return pd.DataFrame()
38
+ df = pd.read_csv(full_path)
39
+ df.columns = [col.lower() for col in df.columns]
40
+ return df
41
 
42
+ @st.cache_data
43
+ def load_all_data():
44
+ """Memuat semua dataset yang diperlukan"""
45
+ file_paths = {
46
+ 'buildings': 'poi_places.csv',
47
+ 'road_nodes': 'road_nodes.csv',
48
+ 'road_edges': 'road_edges.csv'
49
+ }
50
+ data = {}
51
+ for key, fname in file_paths.items():
52
+ data[key] = load_csv_from_local(fname)
53
+ return data
54
+
55
+ # ─── BUILD GRAPH ─────────────────────────────────────────────────────────────
56
  @st.cache_data
57
  def build_graph(gdf_nodes, gdf_edges, gdf_poi):
58
  G = nx.Graph()
59
  # add road nodes
60
+ for _, row in gdf_nodes.iterrows():
61
+ G.add_node(row['osmid'], x=row['x'], y=row['y'])
62
  # add road edges
63
+ for _, row in gdf_edges.iterrows():
64
+ if pd.notna(row['length']):
65
+ G.add_edge(row['u'], row['v'], length=row['length'])
66
+ # add poi nodes
67
+ for idx, row in gdf_poi.iterrows():
68
  nid = f"poi_{idx}"
69
+ G.add_node(nid, x=row['longitude'], y=row['latitude'], name=row.get('name', f"POI {idx}"))
70
+ # connect poi to nearest road node
71
  road_list = [(n, d['y'], d['x']) for n, d in G.nodes(data=True) if isinstance(n, (int, float))]
72
+ for idx, row in gdf_poi.iterrows():
73
  nid = f"poi_{idx}"
74
+ lat, lon = row['latitude'], row['longitude']
75
  nearest = min(road_list, key=lambda x: geodesic((lat, lon), (x[1], x[2])).meters)[0]
76
  dist = geodesic((lat, lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
77
  G.add_edge(nid, nearest, length=dist)
78
  G.add_edge(nearest, nid, length=dist)
79
  return G
80
 
81
+ # ─── HELPERS ──────────────────────────────────────────────────────────────────
82
  def get_poi_id(name, gdf_poi):
83
+ df = gdf_poi[gdf_poi['name'].str.lower().str.strip() == name.lower().strip()]
84
+ return f"poi_{df.index[0]}" if not df.empty else None
85
 
86
+ def compute_route(G, start, end, speed_kmh):
87
+ path = nx.shortest_path(G, start, end, weight='length')
88
+ dist_m = nx.shortest_path_length(G, start, end, weight='length')
89
+ km = dist_m / 1000
90
+ time_min = km / speed_kmh * 60
91
+ return path, km, time_min
92
 
93
+ # ─── MAIN ─────────────────────────────────────────────────────────────────────
94
  # load data
95
+ data = load_all_data()
96
+ gdf_poi = data['buildings']
97
+ gdf_nodes = data['road_nodes']
98
+ gdf_edges = data['road_edges']
99
 
100
  # sidebar inputs
101
  st.sidebar.header("πŸ“ Input Rute")
102
+ if 'name' not in gdf_poi.columns:
103
+ st.sidebar.error("Kolom 'name' tidak ditemukan di poi_places.csv")
104
+ st.stop()
105
+ poi_list = gdf_poi['name'].dropna().tolist()
106
  start = st.sidebar.selectbox("Dari:", poi_list)
107
  end = st.sidebar.selectbox("Ke:", poi_list, index=1)
108
  time_input = st.sidebar.time_input("Waktu Berangkat", value=datetime.now().time())
109
 
110
+ # show selection
111
+ st.write(f"**Start:** {start} β€’ **End:** {end} β€’ **Depart:** {time_input.strftime('%H:%M')}")
112
+
113
+ # on click search
114
  if st.sidebar.button("πŸ” Cari Rute"):
115
  G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
116
+ sid = get_poi_id(start, gdf_poi)
117
+ eid = get_poi_id(end, gdf_poi)
118
+ if not sid or not eid:
119
+ st.error("Lokasi tidak valid.")
120
  else:
121
  # Drive
122
+ p_d, km_d, t_d = compute_route(G, sid, eid, speed_kmh=30)
123
+ cost_d = int(km_d * 3000)
124
  st.subheader("πŸš— Drive Mode")
125
+ st.write(f"Jarak: **{km_d:.2f} km**, Waktu: **{t_d:.1f} menit**, Biaya: **{cost_d:,} IDR**")
126
+ m1 = folium.Map(location=[gdf_poi['latitude'].mean(), gdf_poi['longitude'].mean()], zoom_start=13)
127
+ coords1 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in p_d]
128
  folium.PolyLine(coords1, color='blue', weight=4).add_to(m1)
129
  st_folium(m1, width=700, height=400)
130
  # Bus
131
+ p_b, km_b, t_b = compute_route(G, sid, eid, speed_kmh=25)
132
+ arr = (datetime.combine(datetime.today(), time_input) + timedelta(minutes=t_b)).time()
133
  st.subheader("🚌 Bus Mode")
134
+ st.write(f"Jarak: **{km_b:.2f} km**, Waktu: **{t_b:.1f} menit**, Tiba: **{arr.strftime('%H:%M')}**, Biaya: **3,500 IDR**")
135
+ m2 = folium.Map(location=[gdf_poi['latitude'].mean(), gdf_poi['longitude'].mean()], zoom_start=13)
136
+ coords2 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in p_b]
137
  folium.PolyLine(coords2, color='green', weight=4).add_to(m2)
138
  st_folium(m2, width=700, height=400)
139
  else:
140
+ st.info("Pilih rute dan klik Cari Rute di sidebar.")