Spaces:
Runtime error
Runtime error
| import pandas as pd | |
| import dash | |
| from dash import dcc, html | |
| import webbrowser | |
| from src.viz import Visualizer as viz | |
| import plotly.graph_objects as go | |
| import os | |
| import datetime | |
| import plotly.express as px | |
| import json | |
| start_time = 0#240 | |
| end_time = 1600#600 | |
| start_date = datetime.datetime(2024, 6, 7, 12, 0, 0) | |
| end_date = start_date + datetime.timedelta(seconds=end_time) | |
| # Initialize the Dash app | |
| app = dash.Dash(__name__, suppress_callback_exceptions=True) | |
| # Global dictionary to store data | |
| data_store = {} | |
| def parse_action_trace(path, exclude = []): | |
| """ | |
| Parses action data from CSV files into a dictionary. | |
| This function reads CSV files specified in a dictionary where each key-value pair | |
| corresponds to an condition ID and its associated file. It extracts columns related to | |
| time, action name, agent name, action end time, and attributes from each file and | |
| stores them in a nested dictionary structure. | |
| Args: | |
| files_dict (dict): A dictionary where keys are action IDs and values are file paths. | |
| Returns: | |
| dict: A dictionary where each key is an condition ID and the value is another dictionary | |
| containing parsed data from the corresponding CSV file. | |
| """ | |
| df = pd.read_csv(path, engine = "python") | |
| # Convert to events list for timeline plotting | |
| events = [] | |
| for i in range(len(df['time'])): | |
| if not any(excl in df['actionName'][i] for excl in exclude): | |
| event = { | |
| 'agent': df['agentName'][i], | |
| 'name': df['actionName'][i], | |
| 'start': start_date + datetime.timedelta(seconds=df['time'][i]), | |
| 'end': start_date + datetime.timedelta(seconds=df['time'][i] + max(df['actionEndTime'][i], 2)), | |
| 'duration': max(df['actionEndTime'][i], 2), | |
| 'attributes': df['attributes'][i] | |
| } | |
| events.append(event) | |
| return events | |
| def parse_all_data(path): | |
| # Get the list of subfolders in the 'data' folder | |
| subfolders = [f.name for f in os.scandir(path) if f.is_dir()] | |
| # Load all data at the start of the program | |
| # Load the CSV files into pandas DataFrames from the selected subfolder | |
| data = {} | |
| exclude = ['Detect_crash','Detect_conflict','Flight_Dynamics','Reroute_flight','Show_radar','Direct_to_waypoint','Change_heading','fly'] | |
| for subfolder in subfolders: | |
| data[subfolder] = { | |
| 'wnd_action_trace': pd.read_csv(f'{path}/{subfolder}/actionTrace_Agent_PIC Blaze.csv'), | |
| 'all_action_trace': parse_action_trace(f'{path}/{subfolder}/actionTrace.csv', exclude), | |
| } | |
| data[subfolder]['aircraft_data'] = {} | |
| for file in os.listdir(f'{path}/{subfolder}'): | |
| if file.endswith('_acstate.csv'): | |
| aircraft_id = file.split('_')[0] | |
| data[subfolder]['aircraft_data'][aircraft_id] = pd.read_csv(f'{path}/{subfolder}/{file}') | |
| subfolder_labels = [{'label': i, 'value': i} for i in subfolders] | |
| return subfolder_labels, data | |
| # Create the layout for the app | |
| app.layout = html.Div([ | |
| html.Div([ | |
| html.Div("Input the relative path to your results folder, then click 'Submit':", id='text1'), | |
| dcc.Input( | |
| id='path-input', | |
| value='data', | |
| type='text', | |
| placeholder='Type your path... For example, ../wmc5.1/Scenario/AAMv2/Results', | |
| style={'width': '90%'} # Make the input wider | |
| ), | |
| html.Button('Submit', id='submit-button', n_clicks=0), | |
| # Create a dropdown menu with the subfolders as options | |
| html.Div("Once all subfolders are loaded (representing the different conditions you ran in WMC), you can select which one to plot:", id='text2'), | |
| dcc.Dropdown( | |
| id='subfolder-dropdown', | |
| value=None #subfolders[0] # Default value | |
| ) | |
| ], style={'width': '50%', 'border': '1px solid gray'}), | |
| html.Div([ | |
| html.Div([ | |
| dcc.Graph(id='map'), # Second graph | |
| dcc.Dropdown( | |
| id='variable-dropdown', | |
| # options=[{'label': i, 'value': i} for i in subfolders], | |
| value=None #subfolders[0] # Default value | |
| ), | |
| dcc.Graph(id='altitude-series') # Third graph | |
| ], style={'width': '39%', 'display': 'inline-block', 'border': '1px solid gray'}), | |
| html.Div([ | |
| dcc.Tabs(id='tabs-example-1', value='tab-1', children=[ | |
| dcc.Tab(label='Traditional Action Trace', value='tab-1'), | |
| dcc.Tab(label='What`s Next Diagram', value='tab-2'), | |
| ]), | |
| html.Div(id='tabs-example-content-1'), | |
| html.Div([dcc.Graph(id='timeline')], id='tab-1-content', style={'display': 'none'}), # Fourth graph | |
| html.Div([dcc.Graph(id='time-series')], id='tab-2-content', style={'display': 'none'}), # First graph | |
| ], style={'width': '59%', 'display': 'inline-block', 'border': '1px solid gray'}) | |
| ], style={'display': 'flex'}), | |
| html.Div( | |
| dcc.Slider( | |
| id='time-slider', | |
| min=start_time, | |
| max=end_time, | |
| value=start_time+100, | |
| marks={str(time): str(time) for time in range(start_time, end_time+1, 50)}, | |
| updatemode='drag' | |
| ), | |
| style={'width': '100%'} # Set the width of the div containing the slider to 80% of the container | |
| ) | |
| ]) | |
| def render_content(tab): | |
| if tab == 'tab-1': | |
| return {'display': 'block'}, {'display': 'none'} | |
| elif tab == 'tab-2': | |
| return {'display': 'none'}, {'display': 'block'} | |
| # Load data when the app starts and store it in the hidden div | |
| def load_data(n_clicks, selected_path): | |
| print("selected path is",selected_path) | |
| global data_store | |
| if n_clicks > 0: | |
| if selected_path is None or not os.path.isdir(selected_path): | |
| return [], [] | |
| subfolder_labels, data = parse_all_data(selected_path) | |
| print('subfolders',subfolder_labels) | |
| if data is None: | |
| data = {} | |
| # Store data in the global dictionary | |
| data_store = data | |
| labels = list(data[list(data.keys())[0]]['aircraft_data'][list(data[list(data.keys())[0]]['aircraft_data'].keys())[0]].columns.tolist()) # Extracting column names for the first aircraft-specific dataframe | |
| return subfolder_labels, labels | |
| else: | |
| return [], [] | |
| # Define the callback to update the graphs based on the slider value | |
| # def update_graph(selected_subfolder, selected_time): | |
| # # Load the CSV files into pandas DataFrames from the selected subfolder | |
| # df1 = pd.read_csv(f'data/{selected_subfolder}/actionTrace_Agent_PIC Blaze.csv') | |
| # df2 = pd.read_csv(f'data/{selected_subfolder}/SNLBlaze_acstate.csv') | |
| # df3 = pd.read_csv(f'data/{selected_subfolder}/GCCRaven_acstate.csv') | |
| # exclude = ['Detect_crash','Detect_conflict','Flight_Dynamics','Reroute_flight','Show_radar','Direct_to_waypoint','Change_heading'] | |
| # events = parse_action_trace(f'data/{selected_subfolder}/actionTrace.csv', exclude) | |
| def update_graph(selected_subfolder, selected_time, selected_variable): | |
| if selected_subfolder not in data_store: | |
| return go.Figure(), go.Figure(), go.Figure(layout=dict(autosize=True, width=None, height=300)), go.Figure() | |
| wnd_action_trace = data_store[selected_subfolder].get('wnd_action_trace', pd.DataFrame()) | |
| all_action_trace = data_store[selected_subfolder].get('all_action_trace', []) | |
| aircraft_data = data_store[selected_subfolder].get('aircraft_data', {}) | |
| filtered_wnd_action_trace = wnd_action_trace[(wnd_action_trace['time'] >= start_time) & (wnd_action_trace['time'] <= selected_time)] | |
| filtered_all_action_trace = [] | |
| for event in all_action_trace: | |
| if (event['start'] - start_date).total_seconds() <= selected_time: | |
| event_copy = event.copy() # Create a local copy of the event | |
| if (event_copy['end'] - start_date).total_seconds() > selected_time: | |
| event_copy['end'] = start_date + datetime.timedelta(seconds=selected_time) | |
| filtered_all_action_trace.append(event_copy) | |
| ac_data = {} | |
| for aircraft_id, df in aircraft_data.items(): | |
| filtered_df = df[(df['time'] >= start_time) & (df['time'] <= selected_time)] | |
| ac_data[aircraft_id] = filtered_df | |
| fig1 = go.Figure(layout=dict(autosize=True, width=None, height=500)) | |
| fig1['layout']['uirevision'] = 'Hello world!' | |
| fig1 = viz.plot_trajectory(fig1, ac_data) | |
| fig2 = go.Figure(layout=dict(autosize=True, width=700, height=700)) | |
| fig2 = viz.wnd_visualization(fig2, filtered_wnd_action_trace, selected_time, start_time, end_time) | |
| fig2['layout']['uirevision'] = 'Hello world!' | |
| if selected_variable is not None: | |
| fig3 = go.Figure(layout=dict(autosize=True, width=None, height=300)) | |
| for aircraft_id, df in ac_data.items(): | |
| fig3.add_trace(go.Scatter(x=df['time'], y=df[selected_variable], mode='lines', name=aircraft_id)) | |
| fig3.update_layout(xaxis_title='Real Time', yaxis_title=selected_variable, showlegend=True, xaxis=dict(range=[start_time, end_time]))#, yaxis=dict(range=[0, 1200]) | |
| fig3['layout']['uirevision'] = 'Hello world!' | |
| else: | |
| fig3 = go.Figure(layout=dict(autosize=True, width=None, height=300)) | |
| fig4 = go.Figure(layout=dict(autosize=True, width=None, height=700)) | |
| fig4 = viz.timeline(fig4, filtered_all_action_trace, selected_time, start_date, end_date) | |
| fig4['layout']['uirevision'] = 'Hello world!' | |
| return fig2, fig1, fig3, fig4 | |
| # Run the app | |
| if __name__ == '__main__': | |
| # webbrowser.open_new("http://127.0.0.1:8050/") | |
| app.run_server(debug=False, host='0.0.0.0', port=7860) | |