papa-experiment / app.py
aadii14's picture
Create app.py
c0e9bfa verified
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
# --- Page Config ---
st.set_page_config(
page_title="Locomotive Analytics Dashboard",
page_icon="πŸš„",
layout="wide"
)
# --- Helper Functions ---
@st.cache_data
def load_telemetry_data(file):
try:
df = pd.read_csv(file)
# Standardize columns: remove whitespace
df.columns = df.columns.str.strip()
# 1. Parse Dates
if 'Logging Time' in df.columns:
df['Logging Time'] = pd.to_datetime(df['Logging Time'])
# 2. Drop Outliers/Unwanted Columns as requested
cols_to_drop = ['distFromPrevLatLng', 'last/cur stationCode']
df = df.drop(columns=[c for c in cols_to_drop if c in df.columns], errors='ignore')
# 3. Sort by time
df = df.sort_values(by='Logging Time')
return df
except Exception as e:
st.error(f"Error loading telemetry data: {e}")
return None
@st.cache_data
def load_reference_data(file):
try:
df = pd.read_csv(file)
df.columns = df.columns.str.strip()
return df
except Exception as e:
st.error(f"Error loading reference data: {e}")
return None
# --- Sidebar ---
st.sidebar.title("πŸ”§ Configuration")
st.sidebar.subheader("1. Data Input")
# Allow user to upload, but fallback to local files for demo if available
uploaded_telemetry = st.sidebar.file_uploader("Upload Telemetry CSV", type=['csv'])
uploaded_ref = st.sidebar.file_uploader("Upload OHE/Signal Ref CSV", type=['csv'])
# Logic to load data (User upload > Local default > None)
df = None
df_ref = None
# Load Telemetry
if uploaded_telemetry:
df = load_telemetry_data(uploaded_telemetry)
else:
# Check for local demo file
try:
df = load_telemetry_data("37563 22.12.csv")
st.sidebar.info("Using local demo file: 37563 22.12.csv")
except:
pass
# Load Reference (OHE)
if uploaded_ref:
df_ref = load_reference_data(uploaded_ref)
else:
# Check for local demo file
try:
df_ref = load_reference_data("ohe_master_up.csv")
st.sidebar.info("Using local demo file: ohe_master_up.csv")
except:
pass
# --- Main Dashboard ---
st.title("πŸš„ Indian Railways: Locomotive GPS Analytics")
if df is not None:
# --- Filter Section ---
st.sidebar.subheader("2. Filter Options")
# Device Filter
if 'Device Id' in df.columns:
device_ids = df['Device Id'].unique()
selected_device = st.sidebar.selectbox("Select Device Id", device_ids)
df_filtered = df[df['Device Id'] == selected_device]
else:
df_filtered = df
st.warning("Column 'Device Id' not found. Showing all data.")
# Time Filter
if 'Logging Time' in df_filtered.columns:
min_time = df_filtered['Logging Time'].min()
max_time = df_filtered['Logging Time'].max()
# Ensure we have valid times
if pd.notnull(min_time) and pd.notnull(max_time):
start_time, end_time = st.sidebar.slider(
"Select Time Range",
min_value=min_time.to_pydatetime(),
max_value=max_time.to_pydatetime(),
value=(min_time.to_pydatetime(), max_time.to_pydatetime()),
format="MM-DD HH:mm"
)
mask = (df_filtered['Logging Time'] >= start_time) & (df_filtered['Logging Time'] <= end_time)
df_filtered = df_filtered.loc[mask]
# --- KPIs ---
row1_col1, row1_col2, row1_col3, row1_col4 = st.columns(4)
with row1_col1:
st.metric("Total Data Points", len(df_filtered))
with row1_col2:
max_speed = df_filtered['Speed'].max() if 'Speed' in df_filtered.columns else 0
st.metric("Max Speed", f"{max_speed:.1f} km/h")
with row1_col3:
avg_speed = df_filtered['Speed'].mean() if 'Speed' in df_filtered.columns else 0
st.metric("Avg Speed", f"{avg_speed:.1f} km/h")
with row1_col4:
# Calculate total distance approximated if not available
if 'distFromSpeed' in df_filtered.columns:
# Assuming distFromSpeed is cumulative or segment distance.
# If it is segment distance in meters:
total_dist_km = df_filtered['distFromSpeed'].sum() / 1000
st.metric("Approx Dist (Ref)", f"{total_dist_km:.2f} km")
else:
st.metric("Distance", "N/A")
# --- Charts ---
# 1. Speed vs Time
st.subheader("πŸ“ˆ Speed Profile Analysis")
if 'Speed' in df_filtered.columns and 'Logging Time' in df_filtered.columns:
fig_speed = px.line(
df_filtered,
x='Logging Time',
y='Speed',
title='Speed vs Time',
hover_data=['Latitude', 'Longitude', 'distFromSpeed'],
template='plotly_dark'
)
fig_speed.update_traces(line_color='#00CC96')
st.plotly_chart(fig_speed, use_container_width=True)
# 2. Map Visualization
st.subheader("πŸ—ΊοΈ GPS Trajectory & Infrastructure")
# Create the base map with Locomotive Path
if 'Latitude' in df_filtered.columns and 'Longitude' in df_filtered.columns:
# Base Scatter Mapbox for the Train
fig_map = px.scatter_mapbox(
df_filtered,
lat="Latitude",
lon="Longitude",
color="Speed",
size_max=10,
zoom=10,
hover_data=['Logging Time', 'Speed'],
color_continuous_scale=px.colors.sequential.Plasma,
title="Locomotive GPS Path (Colored by Speed)"
)
# Overlay Reference Data (OHE) if available
if df_ref is not None and 'Latitude' in df_ref.columns and 'Longitude' in df_ref.columns:
# Create a separate scatter trace for OHEs
ref_trace = go.Scattermapbox(
lat=df_ref['Latitude'],
lon=df_ref['Longitude'],
mode='markers',
marker=go.scattermapbox.Marker(
size=8,
color='white',
symbol='circle'
),
text=df_ref['OHEMas'] if 'OHEMas' in df_ref.columns else df_ref.index,
name='OHE Masts'
)
fig_map.add_trace(ref_trace)
# Update Layout
fig_map.update_layout(
mapbox_style="open-street-map",
margin={"r":0,"t":40,"l":0,"b":0},
height=600
)
st.plotly_chart(fig_map, use_container_width=True)
# --- Data View ---
with st.expander("πŸ“„ View Raw Data"):
st.dataframe(df_filtered)
else:
st.info("πŸ‘‹ Please upload a CSV file to begin analysis.")
st.markdown("""
**Expected Columns:** `Device Id`, `Logging Time`, `Latitude`, `Longitude`, `Speed`, `distFromSpeed`
""")