Commit aecbc44a authored by doetschj's avatar doetschj
Browse files

Initial implementation of dashboard functionality

parent 15dee06d
......@@ -20,6 +20,7 @@ from logging.handlers import RotatingFileHandler
from dug_seis.acquisition.acquisition import acquisition_ as acquisition_function
from dug_seis.processing.processing import processing as processing_function
from dug_seis.merge import merge as merge_function
from dug_seis.visualization.dashboard import dashboard as dashboard_function
# shut up libraries
logging.getLogger('requests').setLevel(logging.ERROR)
......@@ -130,3 +131,13 @@ def show_parameters(ctx):
"""
param = ctx.obj['param']
print(yaml.dump(param))
@cli.command()
@click.pass_context
def dashboard(ctx):
"""
Run dashboard to show recent events
"""
param = ctx.obj['param']
dashboard_function(param)
......@@ -317,7 +317,7 @@ class Event(ObsPyEvent):
if self.classification != 'noise':
if save_fig:
fig.savefig(fparam['plot_folder_'+self.classification] + "/event-" + str(self.event_id) + '_' + t_start_plotn + ".png",
fig.savefig(fparam['plot_folder_'+self.classification] + "/event-" + "{:03d}".format(self.event_id) + '_' + t_start_plotn + ".png",
dpi=100)
self.logger.info('Event ' + str(self.event_id) + ': Plotted, Figure saved.')
else:
......@@ -325,7 +325,7 @@ class Event(ObsPyEvent):
else:
if save_fig:
fig.savefig(fparam['noise_vis_folder'] + "/noise vis-" + t_start_plotn + ".png", dpi=100)
fig.savefig(fparam['noise_vis_folder'] + "/noise_vis-" + t_start_plotn + ".png", dpi=100)
self.logger.info('Noise Visualisation: Plotted, Figure saved.')
else:
......@@ -334,9 +334,9 @@ class Event(ObsPyEvent):
def event_save_csv(self, filename='events.csv'):
# write CSV file with CH1903 coordinates
cols = pd.Index(['Event_id', 'datenum', 'x', 'y', 'z', 'Mag', 'loc_rms', 'npicks'], name='cols')
cols = pd.Index(['Event_id', 'Date&Time', 'x', 'y', 'z', 'Mag', 'loc_rms', 'npicks'], name='cols')
if len(self.t0):
t0 = self.t0[-1].matplotlib_date
t0 = self.t0[-1]
x = self.extra['x']['value']
y = self.extra['y']['value']
z = self.extra['z']['value']
......@@ -349,7 +349,7 @@ class Event(ObsPyEvent):
else:
Mag = -99
npicks = len(self.picks)
outdata = [self.event_id] + [t0] + [x] + [y] + [z] + [Mag] + [rms] + [npicks]
outdata = [self.event_id] + [t0] + [np.round(x, 3)] + [np.round(y, 3)] + [np.round(z, 3)] + [np.round(Mag,2)] + [np.round(rms,5)] + [npicks]
df = pd.DataFrame(data=[outdata], columns=cols)
df.to_csv(filename, mode='a', header=False, index=False)
self.logger.info('Event ' + str(self.event_id) + ': Info saved to %s.', filename)
# -*- coding: utf-8 -*-
import dash
from dash.dependencies import Input, Output, State
from dash_table.Format import Format, Scheme
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
import os
import numpy as np
import glob
import flask
from sys import platform
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
static_image_route = '/static/'
def event_table(df, derived_virtual_selected_rows=None):
if derived_virtual_selected_rows is None:
derived_virtual_selected_rows = list(range(len(df.index)))
return dash_table.DataTable(
id='datatable-interactivity',
columns=[
{'id': 'Id', 'name': 'Id'},
{'id': 'Date & Time', 'name': 'Date & Time'},
{'id': 'x', 'name': 'x', 'format': Format(precision=3, scheme=Scheme.fixed)},
{'id': 'y', 'name': 'y', 'format': Format(precision=3, scheme=Scheme.fixed)},
{'id': 'z', 'name': 'z', 'format': Format(precision=3, scheme=Scheme.fixed)},
{'id': 'Mag', 'name': 'Mag', 'format': Format(precision=1, scheme=Scheme.fixed)},
{'id': 'loc_rms', 'name': 'loc_rms', 'format': Format(precision=2, scheme=Scheme.fixed)},
{'id': 'npicks', 'name': 'npicks'}],
n_fixed_rows=1,
data=df.to_dict('rows'),
style_table={
'maxHeight': '300',
'overflowY': 'scroll',
'overflowX': 'scroll',
},
style_header={
# 'backgroundColor': 'white',
'fontWeight': 'bold',
},
style_cell={'textAlign': 'center',
'whiteSpace': 'no-wrap',
'overflow': 'hidden',
'textOverflow': 'ellipsis',
'maxWidth': 0,
},
editable=False,
filtering=True,
sorting=True,
sorting_type='single',
row_selectable='multi',
selected_rows=derived_virtual_selected_rows,
)
def call_event_table():
if os.path.isfile('events.csv'):
df = pd.read_csv('events.csv', header=None,
names=['Id', 'Date & Time', 'x', 'y', 'z', 'Mag', 'loc_rms', 'npicks'])
df['Date & Time'] = pd.to_datetime(df['Date & Time'])
#df.round(pd.Series([1, 0, 2], index=['Mag', 'y', 'z']))
return html.Div(children=event_table(df), id='table-container')
def serve_layout():
event_image_directory = os.getcwd() + '/event_figs_passive/'
list_of_event_images = sorted([os.path.basename(x) for x in glob.glob('{}*.png'.format(event_image_directory))])
if len(list_of_event_images) == 0:
list_of_event_images.append('')
noise_image_directory = os.getcwd() + '/noise_vis/'
list_of_noise_images = sorted([os.path.basename(x) for x in glob.glob('{}*.png'.format(noise_image_directory))])
if len(list_of_noise_images) == 0:
list_of_noise_images.append('')
return html.Div(children=[
html.H1('DUG-Seis Dashboard'),
html.H4('Choose update time for event list:'),
dcc.RadioItems(id='set-time',
value=5000,
options=[
{'label': 'Every second', 'value': 1000},
{'label': 'Every 5 seconds', 'value': 5000},
{'label': 'Every 30 seconds', 'value': 30000},
{'label': 'Off', 'value': 1000000000}
]),
html.P('On update, the selected events are kept, but sorting, filtering and figure view is reset.'),
html.H4('Event List:'),
call_event_table(),
html.Div(id='datatable-interactivity-container'),
html.H4('Event Waveforms:'),
dcc.Dropdown(
id='event-image-dropdown',
options=[{'label': i, 'value': i} for i in list_of_event_images],
value=list_of_event_images[-1]
),
html.Img(id='event-image', style={'width': '100%'}),
html.H4('Noise:'),
dcc.Dropdown(
id='noise-image-dropdown',
options=[{'label': i, 'value': i} for i in list_of_noise_images],
value=list_of_noise_images[-1]
),
html.Img(id='noise-image', style={'width': '100%'}),
dcc.Interval(
id='interval-component',
interval=5 * 1000, # in milliseconds
n_intervals=0
),
])
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config['suppress_callback_exceptions'] = True
app.layout = serve_layout
@app.server.route('{}<image_path>.png'.format(static_image_route))
def serve_image(image_path):
if 'event' in image_path:
image_directory = os.getcwd() + '/event_figs_passive/'
elif 'noise' in image_path:
image_directory = os.getcwd() + '/noise_vis/'
list_of_images = [os.path.basename(x) for x in glob.glob('{}*.png'.format(image_directory))]
image_name = '{}.png'.format(image_path)
if image_name not in list_of_images:
raise Exception('"{}" is excluded from the allowed static files'.format(image_path))
return flask.send_from_directory(image_directory, image_name)
@app.callback(
dash.dependencies.Output('event-image', 'src'),
[dash.dependencies.Input('event-image-dropdown', 'value')])
def update_image_src(value):
return static_image_route + value
@app.callback(
dash.dependencies.Output('noise-image', 'src'),
[dash.dependencies.Input('noise-image-dropdown', 'value')])
def update_image_src(value):
return static_image_route + value
@app.callback(
dash.dependencies.Output('interval-component', 'interval'),
[dash.dependencies.Input('set-time', 'value')])
def update_interval(value):
return value
@app.callback(Output('table-container', 'children'),
[Input('interval-component', 'n_intervals')],
[State('datatable-interactivity', 'derived_virtual_data'),
State('datatable-interactivity', 'derived_virtual_selected_rows')])
def update_table(n, rows, derived_virtual_selected_rows):
if derived_virtual_selected_rows is None:
derived_virtual_selected_rows = []
df = pd.read_csv('events.csv', header=None,
names=['Id', 'Date & Time', 'x', 'y', 'z', 'Mag', 'loc_rms', 'npicks'])
df['Date & Time'] = pd.to_datetime(df['Date & Time'])
dff = pd.DataFrame(rows)
if len(dff.index) < len(df.index):
[derived_virtual_selected_rows.append(i) for i in range(len(dff.index), len(df.index))]
return event_table(df, derived_virtual_selected_rows)
@app.callback([Output('event-image-dropdown', 'options'),
Output('noise-image-dropdown', 'options'),
Output('event-image-dropdown', 'value'),
Output('noise-image-dropdown', 'value')],
[Input('interval-component', 'n_intervals')])
def update_dropdowns(n):
event_image_directory = os.getcwd() + '/event_figs_passive/'
list_of_event_images = sorted([os.path.basename(x) for x in glob.glob('{}*.png'.format(event_image_directory))])
if len(list_of_event_images) == 0:
list_of_event_images.append('')
event_options = [{'label': i, 'value': i} for i in list_of_event_images]
noise_image_directory = os.getcwd() + '/noise_vis/'
list_of_noise_images = sorted([os.path.basename(x) for x in glob.glob('{}*.png'.format(noise_image_directory))])
if len(list_of_noise_images) == 0:
list_of_noise_images.append('')
noise_options = [{'label': i, 'value': i} for i in list_of_noise_images]
return event_options, noise_options, list_of_event_images[-1], list_of_noise_images[-1]
@app.callback(Output('datatable-interactivity-container', 'children'),
[Input('datatable-interactivity', 'derived_virtual_data'),
Input('datatable-interactivity', 'derived_virtual_selected_rows')])
def update_graph(rows, derived_virtual_selected_rows):
# When the table is first rendered, `derived_virtual_data` and
# `derived_virtual_selected_rows` will be `None`. This is due to an
# idiosyncracy in Dash (unsupplied properties are always None and Dash
# calls the dependent callbacks when the component is first rendered).
# So, if `rows` is `None`, then the component was just rendered
# and its value will be the same as the component's dataframe.
# Instead of setting `None` in here, you could also set
# `derived_virtual_data=df.to_rows('dict')` when you initialize
# the component.
if derived_virtual_selected_rows is None:
derived_virtual_selected_rows = []
df = pd.read_csv('events.csv', header=None,
names=['Event_id', 'Date & Time', 'x', 'y', 'z', 'Mag', 'loc_rms', 'npicks'])
df['Date & Time'] = pd.to_datetime(df['Date & Time'])
if rows is not None:
dff = pd.DataFrame(rows)
if len(dff.index) < len(df.index):
[derived_virtual_selected_rows.append(i) for i in range(len(df.index), len(df.index))]
df = df.iloc[derived_virtual_selected_rows]
Mag_ref = 4
size_ref = 12
return html.Div([
html.Div([
# Magnitude graph
dcc.Graph(
id='Mag',
figure={
'data': [
{
'x': df['Date & Time'],
'y': df['Mag'],
'mode': 'markers',
'marker': {
'size': df['Mag']/Mag_ref*size_ref,
'cmin': np.min(-df['Mag']),
'cmax': np.max(-df['Mag']),
'color': -df['Mag'],
'colorscale': 'Viridis',
'line': {'width': 1, 'color': 'rgb(0,0,0)'},
}
}
],
'layout': {
'title': 'Magnitude development over time',
'xaxis': {'automargin': True, 'title': 'Time'},
'yaxis': {'automargin': True, 'title': 'Magnitude', 'zeroline': False},
'height': 400,
},
},
)], style={'width': '49%', 'display': 'inline-block'},
),
html.Div([
# Frequency Magnitude Distribution
dcc.Graph(
id='FMD',
figure={
'data': [
{
'x': np.sort(df['Mag']),
'y': list(range(len(df.index), 0, -1)),
}
],
'layout': {
'title': 'Frequency Magnitude Distribution',
'xaxis': {'automargin': True, 'title': 'Magnitude'},
'yaxis': {'automargin': True, 'title': 'Number of events', 'type': 'log'},
'height': 400,
# 'margin': {'t': 50, 'l': 10, 'r': 10},
},
},
)], style={'width': '49%', 'display': 'inline-block'},
),
html.Div([
# Top view
dcc.Graph(
id='top view',
figure={
'data': [
{
'x': df['x'],
'y': df['y'],
'mode': 'markers',
'marker': {
'size': df['Mag']/Mag_ref*size_ref,
'cmin': np.min(-df['Mag']),
'cmax': np.max(-df['Mag']),
'color': -df['Mag'],
'colorscale': 'Viridis',
'line': {'width': 1, 'color': 'rgb(0,0,0)'},
}
}
],
'layout': {
'title': 'Top view',
'xaxis': {'automargin': True, 'title': 'x [m]', 'zeroline': False},
'yaxis': {'automargin': True, 'title': 'y [m]', 'scaleanchor': 'x', 'scaleratio': 1, 'zeroline': False},
'height': 400,
#'margin': {'t': 10, 'l': 10, 'r': 10},
},
},
)], style={'width': '33%', 'display': 'inline-block'},
),
html.Div([
# Top view
dcc.Graph(
id='XZ view',
figure={
'data': [
{
'x': df['x'],
'y': df['z'],
'mode': 'markers',
'marker': {
'size': df['Mag']/Mag_ref*size_ref,
'cmin': np.min(-df['Mag']),
'cmax': np.max(-df['Mag']),
'color': -df['Mag'],
'colorscale': 'Viridis',
'line': {'width': 1, 'color': 'rgb(0,0,0)'},
}
}
],
'layout': {
'title': 'Side view (xz)',
'xaxis': {'automargin': True, 'title': 'x [m]', 'zeroline': False},
'yaxis': {'automargin': True, 'title': 'z [m]', 'scaleanchor': 'x', 'scaleratio': 1, 'zeroline': False},
'height': 400,
#'margin': {'t': 10, 'l': 10, 'r': 10},
},
},
)], style={'width': '33%', 'display': 'inline-block'},
),
html.Div([
# Top view
dcc.Graph(
id='YZ view',
figure={
'data': [
{
'x': df['y'],
'y': df['z'],
'mode': 'markers',
'marker': {
'size': df['Mag']/Mag_ref*size_ref,
'cmin': np.min(-df['Mag']),
'cmax': np.max(-df['Mag']),
'color': -df['Mag'],
'colorscale': 'Viridis',
'line': {'width': 1, 'color': 'rgb(0,0,0)'},
}
}
],
'layout': {
'title': 'Side view (yz)',
'xaxis': {'automargin': True, 'title': 'y [m]', 'zeroline': False},
'yaxis': {'automargin': True, 'title': 'z [m]', 'scaleanchor': 'x', 'scaleratio': 1, 'zeroline': False},
'height': 400,
#'margin': {'t': 10, 'l': 10, 'r': 10},
},
},
)], style={'width': '33%', 'display': 'inline-block'},
),
html.Div([
# 3D scatter plot
dcc.Graph(
id='3D',
figure={
'data': [
{
'x': df['x'],
'y': df['y'],
'z': df['z'],
'mode': 'markers',
'type': 'scatter3d',
'marker': {
'size': df['Mag'] / Mag_ref * size_ref,
'cmin': np.min(-df['Mag']),
'cmax': np.max(-df['Mag']),
'color': -df['Mag'],
'colorscale': 'Viridis',
'line': {'width': 1, 'color': 'rgb(0,0,0)'},
}
}
],
'layout': {
'title': '3D scatter of events',
'xaxis': {'automargin': True, 'title': 'x [m]', 'zeroline': False},
'yaxis': {'automargin': True, 'title': 'y [m]', 'scaleanchor': 'x', 'scaleratio': 1, 'zeroline': False},
'zaxis': {'automargin': True, 'title': 'z [m]', 'scaleanchor': 'x', 'scaleratio': 1, 'zeroline': False},
'height': 600,
# 'margin': {'t': 10, 'l': 10, 'r': 10},
},
},
)], style={'width': '100%', 'display': 'inline-block'},
),
]
)
def dashboard(param):
if platform == "darwin":
os.system('open http://127.0.0.1:8050/')
app.run_server(debug=False)
......@@ -40,7 +40,9 @@ _install_requires = [
"pandas",
"pyproj",
"celery",
"redis",]
"redis",
"dash",
"flask"]
setup(
name='DUG-Seis',
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment