Skip to content

Commit d4234df

Browse files
committed
feat: add District Stations Streamlit app and iframe
the District Stations Streamlit app is a simple proof-of-concept that displays the traffic data collection stations in a district.
1 parent c94d85a commit d4234df

File tree

10 files changed

+12403
-17
lines changed

10 files changed

+12403
-17
lines changed

pems/districts/templates/districts/district.html

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,13 @@
99
<div class="col-lg-4 border">
1010
<h2>Form</h2>
1111
</div>
12-
<div class="col-lg-8 border">
13-
<h2>Chart</h2>
14-
</div>
1512
</div>
16-
<div class="row">
17-
<div class="col-lg-4 border">
18-
<h2>Details for {{ current_district.name }}</h2>
19-
</div>
20-
<div class="col-lg-8 border">
21-
<h2>Map</h2>
13+
<div class="row" style="min-height: 450px;">
14+
<div class="col-lg-12 border">
15+
<iframe class="streamlit-app" src="http://localhost:8501/stations--stations?embed=true&district_number={{ current_district.number }}">
16+
</iframe>
2217
</div>
18+
2319
</div>
20+
</div>
2421
{% endblock districts-content %}

pems/districts/templates/districts/index.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ <h2>Form</h2>
2323
<h2>Chart</h2>
2424
</div>
2525
</div>
26-
<div class="row">
27-
<div class="col-lg-4 border">
28-
<h2>Details</h2>
29-
</div>
30-
<div class="col-lg-8 border">
31-
<h2>Map</h2>
26+
<div class="row" style="min-height: 450px;">
27+
<div class="col-lg-12 border">
28+
<iframe class="streamlit-app" src="http://localhost:8501/stations--stations?embed=true">
29+
</iframe>
3230
</div>
31+
3332
</div>
34-
{% endblock districts-content %}
33+
</div>
34+
{% endblock districts-content %}
3535

36-
</div>
3736
</div>
3837
</div>
38+
</div>
3939
{% endblock inner-content %}

pems/static/css/styles.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ header {
1010
}
1111
}
1212
}
13+
14+
.streamlit-app {
15+
width: 100%;
16+
height: 100%;
17+
}

streamlit_app/apps/stations/__init__.py

Whitespace-only changes.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from pathlib import Path
2+
3+
import os
4+
import pandas as pd
5+
import streamlit as st
6+
7+
8+
APP_DIR = Path(__file__).parent
9+
10+
11+
@st.cache_data()
12+
def fetch_data():
13+
all_files = os.listdir(APP_DIR)
14+
# Get station metadata files
15+
station_files = [f for f in all_files if "_text_meta_" in f and f.endswith(".txt")]
16+
17+
df_list = []
18+
for f in station_files:
19+
file_path = os.path.join(APP_DIR, f)
20+
df = pd.read_csv(file_path, delimiter="\t")
21+
df_list.append(df)
22+
# Combine into a single DataFrame
23+
df_all = pd.concat(df_list, ignore_index=True)
24+
25+
data = df_all.dropna(subset=["Latitude", "Longitude"])
26+
data.loc[:, "State_PM"] = data["State_PM"].astype(str)
27+
data.loc[:, "User_ID_1"] = data["User_ID_1"].astype(str)
28+
return data
29+
30+
31+
query_params = st.query_params
32+
district_number = query_params.get("district_number", "")
33+
district_number = int(district_number) if district_number else district_number # Ensure district_number is an integer
34+
35+
st.set_page_config(layout="wide")
36+
37+
df = fetch_data()
38+
if district_number:
39+
# filter to just the current district
40+
df = df[df["District"] == district_number]
41+
st.title(f"District {district_number} Station Viewer")
42+
else:
43+
st.title("Districts Station Viewer")
44+
45+
left_col, center_col, right_col = st.columns([1, 2, 2])
46+
47+
with left_col:
48+
# Create filters
49+
id_options = ["All"] + sorted(df["ID"].dropna().unique().tolist())
50+
selected_id = st.selectbox("Select Station", id_options)
51+
52+
fwy_options = ["All"] + sorted(df["Fwy"].dropna().unique().tolist())
53+
selected_fwy = st.selectbox("Select Freeway", fwy_options)
54+
55+
dir_options = ["All"] + sorted(df["Dir"].dropna().unique().tolist())
56+
selected_dir = st.selectbox("Select Direction", dir_options)
57+
58+
type_options = ["All"] + sorted(df["Type"].dropna().unique().tolist())
59+
selected_type = st.selectbox("Select Type", type_options)
60+
61+
# Apply filters
62+
filtered_df = df.copy()
63+
64+
if selected_id != "All":
65+
filtered_df = filtered_df[filtered_df["ID"] == selected_id]
66+
67+
if selected_fwy != "All":
68+
filtered_df = filtered_df[filtered_df["Fwy"] == selected_fwy]
69+
70+
if selected_dir != "All":
71+
filtered_df = filtered_df[filtered_df["Dir"] == selected_dir]
72+
73+
if selected_type != "All":
74+
filtered_df = filtered_df[filtered_df["Type"] == selected_type]
75+
76+
with center_col:
77+
# Show filtered data
78+
st.write(f"**Stations:** {filtered_df.shape[0]:,.0f}")
79+
st.write(f"**Directional distance:** {filtered_df["Length"].sum():,.1f} mi")
80+
st.dataframe(filtered_df, use_container_width=True)
81+
82+
with right_col:
83+
# Rename columns to match Streamlit's expected format
84+
map_df = filtered_df.rename(columns={"Latitude": "latitude", "Longitude": "longitude"})
85+
st.map(map_df[["latitude", "longitude"]])

0 commit comments

Comments
 (0)