Spaces:
Sleeping
Sleeping
| 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 --- | |
| 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 | |
| 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` | |
| """) |