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

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +42 -43
src/streamlit_app.py CHANGED
@@ -10,28 +10,29 @@ from datetime import datetime, timedelta
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()
@@ -41,47 +42,45 @@ def load_csv_from_local(file_path):
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')
@@ -90,14 +89,14 @@ def compute_route(G, start, end, speed_kmh):
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")
@@ -107,10 +106,10 @@ 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)
@@ -118,22 +117,22 @@ if st.sidebar.button("πŸ” Cari Rute"):
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:
 
10
  import warnings
11
  warnings.filterwarnings("ignore")
12
 
13
+ # ── SUPPRESS PERMISSION & TELEMETRY ERRORS ──────────────────────────────────
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
+ os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false"
19
 
20
+ # ── STREAMLIT PAGE CONFIG ──────────────────────────────────────────────────
21
  st.set_page_config(
22
  page_title="MODA Multimodal Routing",
23
  layout="wide",
24
  initial_sidebar_state="expanded"
25
  )
26
 
27
+ # ── PATH SETUP ──────────────────────────────────────────────────────────────
28
  BASE_DIR = Path(__file__).resolve().parent
29
  DATA_DIR = BASE_DIR.parent / "data"
30
 
31
+ # ── DATA LOADING ────────────────────────────────────────────────────────────
32
  @st.cache_data
33
+ def load_csv_from_local(filename):
34
+ """Load a single CSV from data directory with caching"""
35
+ full_path = DATA_DIR / filename
36
  if not full_path.exists():
37
  st.error(f"File tidak ditemukan: {full_path}")
38
  return pd.DataFrame()
 
42
 
43
  @st.cache_data
44
  def load_all_data():
45
+ """Load all required datasets at once"""
46
+ files = {
47
  'buildings': 'poi_places.csv',
48
  'road_nodes': 'road_nodes.csv',
49
  'road_edges': 'road_edges.csv'
50
  }
51
+ data = {key: load_csv_from_local(fname) for key, fname in files.items()}
 
 
52
  return data
53
 
54
+ # ── BUILD GRAPH ─────────────────────────────────────────────────────────────
55
  @st.cache_data
56
  def build_graph(gdf_nodes, gdf_edges, gdf_poi):
57
  G = nx.Graph()
58
  # add road nodes
59
+ for _, r in gdf_nodes.iterrows():
60
+ G.add_node(r.osmid, x=r.x, y=r.y)
61
  # add road edges
62
+ for _, r in gdf_edges.iterrows():
63
+ if pd.notna(r.length):
64
+ G.add_edge(r.u, r.v, length=r.length)
65
+ # add POI nodes
66
+ for idx, r in gdf_poi.iterrows():
67
  nid = f"poi_{idx}"
68
+ G.add_node(nid, x=r.longitude, y=r.latitude, name=r.name)
69
+ # connect POI to nearest road
70
+ roads = [(n, d['y'], d['x']) for n, d in G.nodes(data=True) if isinstance(n, (int, float))]
71
+ for idx, r in gdf_poi.iterrows():
72
  nid = f"poi_{idx}"
73
+ lat, lon = r.latitude, r.longitude
74
+ nearest = min(roads, key=lambda x: geodesic((lat, lon), (x[1], x[2])).meters)[0]
75
  dist = geodesic((lat, lon), (G.nodes[nearest]['y'], G.nodes[nearest]['x'])).meters
76
  G.add_edge(nid, nearest, length=dist)
77
  G.add_edge(nearest, nid, length=dist)
78
  return G
79
 
80
+ # ── ROUTING HELPERS ──────────────────────────────────────────────────────────
81
  def get_poi_id(name, gdf_poi):
82
+ match = gdf_poi[gdf_poi['name'].str.lower().str.strip() == name.lower().strip()]
83
+ return f"poi_{match.index[0]}" if not match.empty else None
84
 
85
  def compute_route(G, start, end, speed_kmh):
86
  path = nx.shortest_path(G, start, end, weight='length')
 
89
  time_min = km / speed_kmh * 60
90
  return path, km, time_min
91
 
92
+ # ── MAIN APP ─────────────────────────────────────────────────────────────────
93
+ # Load datasets
94
  data = load_all_data()
95
  gdf_poi = data['buildings']
96
  gdf_nodes = data['road_nodes']
97
  gdf_edges = data['road_edges']
98
 
99
+ # Sidebar for user input
100
  st.sidebar.header("πŸ“ Input Rute")
101
  if 'name' not in gdf_poi.columns:
102
  st.sidebar.error("Kolom 'name' tidak ditemukan di poi_places.csv")
 
106
  end = st.sidebar.selectbox("Ke:", poi_list, index=1)
107
  time_input = st.sidebar.time_input("Waktu Berangkat", value=datetime.now().time())
108
 
109
+ # Display selection
110
+ st.write(f"**Start:** {start} β€’ **End:** {end} β€’ **Depart:** {time_input.strftime('%H:%M')}")
111
 
112
+ # Trigger routing
113
  if st.sidebar.button("πŸ” Cari Rute"):
114
  G = build_graph(gdf_nodes, gdf_edges, gdf_poi)
115
  sid = get_poi_id(start, gdf_poi)
 
117
  if not sid or not eid:
118
  st.error("Lokasi tidak valid.")
119
  else:
120
+ # Drive Mode
121
+ path_d, km_d, t_d = compute_route(G, sid, eid, speed_kmh=30)
122
  cost_d = int(km_d * 3000)
123
  st.subheader("πŸš— Drive Mode")
124
  st.write(f"Jarak: **{km_d:.2f} km**, Waktu: **{t_d:.1f} menit**, Biaya: **{cost_d:,} IDR**")
125
  m1 = folium.Map(location=[gdf_poi['latitude'].mean(), gdf_poi['longitude'].mean()], zoom_start=13)
126
+ coords1 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_d]
127
  folium.PolyLine(coords1, color='blue', weight=4).add_to(m1)
128
  st_folium(m1, width=700, height=400)
129
+ # Bus Mode
130
+ path_b, km_b, t_b = compute_route(G, sid, eid, speed_kmh=25)
131
+ arrival = (datetime.combine(datetime.today(), time_input) + timedelta(minutes=t_b)).time()
132
  st.subheader("🚌 Bus Mode")
133
+ st.write(f"Jarak: **{km_b:.2f} km**, Waktu: **{t_b:.1f} menit**, Tiba: **{arrival.strftime('%H:%M')}**, Biaya: **3,500 IDR**")
134
  m2 = folium.Map(location=[gdf_poi['latitude'].mean(), gdf_poi['longitude'].mean()], zoom_start=13)
135
+ coords2 = [(G.nodes[n]['y'], G.nodes[n]['x']) for n in path_b]
136
  folium.PolyLine(coords2, color='green', weight=4).add_to(m2)
137
  st_folium(m2, width=700, height=400)
138
  else: