dashboard-test / src /viz.py
mijtsma3's picture
First files for testing from WND
9f4ea7f verified
Raw History Blame Contribute Delete
10 kB
import webbrowser
import dash as dcc
import dash as html
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
import networkx as nx
import datetime
class Visualizer:
def wnd_visualization(figure_handle, dataframe, current_time, start_time = 0, end_time = 10^6):
# Diagonal line y=x
figure_handle.add_trace(go.Scatter(x=[start_time, end_time], y=[start_time, end_time], mode='lines', line=dict(color='black', dash='dash')))
# Dotted lines from the point to axes
figure_handle.add_trace(go.Scatter(x=[current_time, current_time, start_time], y=[start_time, current_time, current_time], mode='lines', line=dict(color='black', dash='dot')))
for time, name in dataframe[['time', 'actionName']].values:
figure_handle.add_trace(go.Scatter(x=[time], y=[time], mode='markers', marker=dict(color='black', size=10)))
timestamps_and_ids_dict = {}
for index, row in dataframe.iterrows():
array_data = row['attributes']
if isinstance(array_data, str):
timestamp_id_triples = []
for pair in array_data.split(';'):
triple = pair.split('|')
triple.append("")
timestamp_id_triples.append(triple)
for triple in timestamp_id_triples:
if '(' in triple[1]:
triple[1], triple[2] = triple[1].split('(')
else:
timestamp_id_triples = []
timestamp_id_triples.sort(key=lambda x: float(x[0][0]))
timestamp_id_dict = {float(triple[0]): str(triple[1]) for triple in timestamp_id_triples}
timestamps_and_ids_dict[row['time']] = timestamp_id_dict
timestamps_before_simulation_time = {timestamp: data for timestamp, data in timestamps_and_ids_dict.items() if timestamp <= current_time}
print(timestamps_before_simulation_time)
mental_model_over_time = {}
if timestamps_before_simulation_time:
for mm_time, array_timestamps_and_ids in timestamps_before_simulation_time.items():
for timestamp, value in array_timestamps_and_ids.items():
if value in mental_model_over_time:
# Value is already in the dictionary, append the timestamp to the existing list
mental_model_over_time[value].append((mm_time, timestamp))
else:
# Value is not in the dictionary, create a new list with the timestamp
mental_model_over_time[value] = [(mm_time, timestamp)]
# Get last update on mental model
previous_timestamp, array_timestamps_and_ids = max(timestamps_before_simulation_time.items(), key=lambda x: x[0])
# Assuming mental model does not change in between, load this as current mental model into dictionary
for timestamp, value in array_timestamps_and_ids.items():
if value in mental_model_over_time:
# Value is already in the dictionary, append the timestamp to the existing list
mental_model_over_time[value].append((current_time, timestamp))
else:
# Value is not in the dictionary, create a new list with the timestamp
mental_model_over_time[value] = [(current_time, timestamp)]
# Add text box with name of event
figure_handle.add_annotation(x=current_time, y=timestamp, text=value, showarrow=True, arrowhead=1, arrowcolor='black', arrowwidth=2)
for label, coordinates in mental_model_over_time.items():
for i in range(len(coordinates) - 1):
x = [coordinates[i][0], coordinates[i + 1][0], coordinates[i + 1][0]]
y = [coordinates[i][1], coordinates[i][1], coordinates[i + 1][1]]
if all(xi <= yi for xi, yi in zip(x, y)):
figure_handle.add_trace(go.Scatter(x=x, y=y, mode='lines', line=dict(color='blue'), marker_symbol='triangle-up'))
if all(xi >= yi for xi, yi in zip(x, y)):
figure_handle.add_trace(go.Scatter(x=x, y=y, mode='lines', line=dict(color='gray'), marker_symbol='circle'))
figure_handle.update_layout(
xaxis_title='Real Time',
yaxis_title='Comprehension of Timeline of Events',
showlegend=False,
xaxis=dict(
range=[start_time,end_time]
),
yaxis=dict(
range=[start_time,end_time],
scaleanchor="x",
scaleratio=1
)
)
return figure_handle
def plot_trajectory(figure_handle, parsed_data, uptime=float('inf'), background_image=None):
fig = figure_handle
if background_image:
fig.add_layout_image(
source=background_image,
xref="x",
yref="y",
x=-96.88,
y=33.16,
sizex=0.23,
sizey=0.44,
sizing="fill",
opacity=0.5,
layer="below"
)
else:
fig.update_layout(
mapbox_style="open-street-map",
mapbox_center_lon=-96.765,
mapbox_center_lat=32.95,
mapbox_zoom=10
)
# Create a graph for the flight plans
G = nx.Graph()
# Define waypoints and their positions with new names from the embedded code
waypoints = {
"NTI": (-96.7535, 32.928), "NTHW": (-96.749, 32.8573), "RUBL": (-96.7588, 32.78),
"HW342": (-96.8003, 32.7508), "TLWY": (-96.807, 32.769), "4DT": (-96.8508, 32.846),
"T57": (-96.686, 32.888), "FSC": (-96.8213, 33.1413), "PLN": (-96.7275, 33.0297)
}
# Add edges between waypoints to the graph with updated names
edges = [("NTI", "NTHW"), ("NTHW", "RUBL"), ("RUBL", "HW342"), ("HW342", "TLWY"),
("TLWY", "4DT"), ("T57", "NTI"), ("FSC", "PLN"),("PLN","NTI")]
G.add_edges_from(edges)
# Draw the graph with smaller waypoint symbols in gray and keep the node labels
for edge in G.edges():
x_values = [waypoints[edge[0]][0], waypoints[edge[1]][0]]
y_values = [waypoints[edge[0]][1], waypoints[edge[1]][1]]
fig.add_trace(go.Scattermapbox(lon=x_values, lat=y_values, mode='lines', line=dict(color='gray'), showlegend=False))#, dash='dash'
for node in G.nodes():
x_value = waypoints[node][0]
y_value = waypoints[node][1]
fig.add_trace(go.Scattermapbox(lon=[x_value], lat=[y_value], mode='markers', marker=dict(size=5, color='gray'), showlegend=False))#, symbol='pentagon'
# for node, (x_value, y_value) in waypoints.items():
# fig.add_annotation(lon=x_value, lat=y_value, text=node, showarrow=False, font=dict(size=6, color='gray'), xanchor='left', yanchor='bottom')
# Add legend label for waypoints and corridors
legend_labels = []
legend_labels.append("Corridors")
legend_labels.append("Waypoints")
colors = ['blue', 'red', 'green', 'purple', 'orange', 'yellow', 'pink', 'cyan', 'magenta', 'brown']
color_index = 0
for ac_name, data in parsed_data.items():
mask = (data['time'] >= 0) & (data['time'] <= uptime)
filtered_xdata = data['longitude_deg'][mask]
filtered_ydata = data['latitude_deg'][mask]
filtered_heading = data['heading_deg'][mask]
filtered_altitude = data['altitude_ft'][mask] # In case we ever want to do 3D visualizations
fig.add_trace(go.Scattermapbox(#Scatter3d possibly for 3D but issue with Scattermapbox not being compatible
mode="lines",
lon=filtered_xdata,
lat=filtered_ydata,
line=dict(width=2, color=colors[color_index]),
name=ac_name
))
if len(filtered_xdata) > 0 and len(filtered_ydata) > 0:
current_heading = filtered_heading.iloc[-1]
fig.add_trace(go.Scattermapbox(
mode="markers",
lon=[filtered_xdata.iloc[-1]],
lat=[filtered_ydata.iloc[-1]],
marker=dict(size=10, color=colors[color_index]),#, symbol=['cross']), #To make custom markers, you need MapBox access token (to use any of Mapbox's tools, APIs, or SDK)
name=ac_name + " Current Location"
))
color_index = (color_index + 1) % len(colors)
fig.update_layout(
mapbox=dict(
center=dict(lon=-96.765, lat=32.95),
zoom=9
),
margin=dict(l=20, r=20, t=20, b=20)
)
return fig
def timeline(figure_handle, events, current_time = 0, start_date = 0, end_date = 0):
category_order = sorted(list(set([e['agent'] for e in events])))
figure_handle = px.timeline(
events, x_start="start", x_end="end", y="agent",
hover_data=['attributes', 'duration'],
color='name',# height=400, width=1600,
category_orders={'agent': category_order}
)
# Add a vertical dashed line at x = current_time
current_datetime = start_date + datetime.timedelta(seconds=current_time)
figure_handle.add_shape(
type="line",
x0=current_datetime, y0=0,
x1=current_datetime, y1=1,
yref="paper",
line=dict(
color="Black",
width=2,
dash="dash",
)
)
figure_handle.update_layout(
xaxis_range=[start_date, end_date]#,
# width=600,
# height=500
)
return figure_handle