dashboard-test / src /viz.py
mijtsma3's picture
First files for testing from WND
9f4ea7f verified
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