"""This file contains the code to run the SpRIT app (via streamlit) both locally and on the web."""
import base64
import copy
import datetime
import importlib
import inspect
import io
import os
import pathlib
import pickle
import sys
import tempfile
import zoneinfo
import matplotlib
import numpy as np
import pandas as pd
import plotly.express as px
from plotly.express import scatter as pxScatter
from plotly.express import timeline as pxTimeline
from plotly.graph_objects import Heatmap as goHeatmap
from plotly.graph_objs._figurewidget import FigureWidget
from plotly.subplots import make_subplots
import streamlit as st
from obspy import UTCDateTime
from obspy.signal.spectral_estimation import PPSD
from scipy import signal
try:
import sprit
from sprit import sprit_hvsr
from sprit import sprit_plot
except Exception:
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parent_dir)
import sprit
from sprit import sprit_hvsr
from sprit import sprit_plot
VERBOSE = False
RESOURCE_DIR = pathlib.Path(str(importlib.resources.files('sprit'))).joinpath('resources')
SAMPLE_DATA_DIR = RESOURCE_DIR.joinpath('sample_data')
SETTINGS_DIR = RESOURCE_DIR.joinpath('settings')
DEFAULT_BAND_LIST = list(sprit_hvsr.DEFAULT_BAND)
spritLogoPath = RESOURCE_DIR.joinpath("icon").joinpath("SpRITLogo.png")
if VERBOSE:
print('Start of file, session state length: ', len(st.session_state.keys()))
PARAM2PRINT = None
[docs]
def print_param(key=PARAM2PRINT, write_key=False):
if key is None:
pass
elif key in st.session_state.keys():
print(key, st.session_state[key], 'type:', type(st.session_state[key]))
if write_key:
st.write(key, st.session_state[key], 'type:', type(st.session_state[key]))
print_param(PARAM2PRINT)
[docs]
def main():
if spritLogoPath.exists():
st.logo(image=spritLogoPath, size='large',
link=r"https://github.com/RJbalikian/SPRIT-HVSR",
icon_image=spritLogoPath)
icon = spritLogoPath
else:
icon = ":material/electric_bolt:"
if 'sprit' in sys.modules and hasattr(sprit, '__version__'):
spritversion = sprit.__version__
else:
spritversion = '2.0+'
aboutStr = """
# About SpRIT
### This app uses SpRIT v0.0.0
SpRIT is developed by Riley Balikian at the Illinois State Geological Survey.
Please visit the following links for any questions:
* [App user guide](https://github.com/RJbalikian/sprit-streamlit/wiki)
* [API Documentation](https://sprit.readthedocs.io/en/latest/sprit.html)
* [Wiki](https://github.com/RJbalikian/SPRIT-HVSR/wiki)
* [Pypi Repository](https://pypi.org/project/sprit/)
"""
aboutStr = aboutStr.replace('0.0.0', spritversion)
if VERBOSE:
print('Start setting up page config, session state length: ', len(st.session_state.keys()))
st.set_page_config('SpRIT HVSR',
page_icon=icon,
layout='wide',
menu_items={'Get help': 'https://github.com/RJbalikian/SPRIT-HVSR/wiki',
'Report a bug': "https://github.com/RJbalikian/SPRIT-HVSR/issues",
'About': aboutStr})
if VERBOSE:
print('Start setting up constants/variables, session state length: ', len(st.session_state.keys()))
OBSPYFORMATS = ['AH', 'ALSEP_PSE', 'ALSEP_WTH', 'ALSEP_WTN', 'CSS', 'DMX',
'GCF', 'GSE1', 'GSE2', 'KINEMETRICS_EVT', 'KNET', 'MSEED',
'NNSA_KB_CORE', 'PDAS', 'PICKLE', 'Q', 'REFTEK130', 'RG16',
'SAC', 'SACXY', 'SEG2', 'SEGY', 'SEISAN', 'SH_ASC', 'SLIST',
'SU', 'TRC',
'TSPAIR', 'WAV', 'WIN', 'Y']
bandVals = [0.05, 0.06, 0.07, 0.08, 0.09,
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9,
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
# SETUP KWARGS
if VERBOSE:
print('Start setting up kwargs dicts, session state length: ', len(st.session_state.keys()))
run_kwargs = {}
ip_kwargs = {}
fd_kwargs = {}
ca_kwargs = {}
rn_kwargs = {}
gpsd_kwargs = {}
phvsr_kwargs = {}
roc_kwargs = {}
cp_kwargs = {}
gr_kwargs = {}
run_kwargs = {}
if VERBOSE:
print('Start getting default values, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
# Get default values
funList = [[sprit_hvsr.run, run_kwargs], [sprit_hvsr.input_params, ip_kwargs],
[sprit_hvsr.fetch_data, fd_kwargs], [sprit_hvsr.calculate_azimuth, ca_kwargs],
[sprit_hvsr.remove_noise, rn_kwargs], [sprit_hvsr.generate_psds, gpsd_kwargs],
[PPSD, gpsd_kwargs], [sprit_hvsr.process_hvsr, phvsr_kwargs],
[sprit_hvsr.remove_outlier_curves, roc_kwargs],
[sprit_hvsr.check_peaks, cp_kwargs], [sprit_hvsr.get_report, gr_kwargs]]
# Function to initialize session state variables
def initial_setup_fun(session_state_key, initial_value, running_value='Do not use'):
if not hasattr(st.session_state, session_state_key):
st.session_state[session_state_key] = initial_value
elif running_value != "Do not use":
st.session_state[session_state_key] = running_value
# Initialize variables
initial_setup_fun('initial_setup', True, False)
initial_setup_fun('tabs_setup', False)
initial_setup_fun('mainContain_setup', False)
def setup_session_state():
if st.session_state.initial_setup:
# "Splash screen" (only shows at initial startup)
mainContainerInitText = """
# SpRIT HVSR
sprit_logo
## About
SpRIT HVSR is developed by the Illinois State Geological Survey, part of the Prairie Research Institute at the University of Illinois.
## For help with app usage, please visit the app user guide [here](https://github.com/RJbalikian/sprit-streamlit/wiki).
### Related Links
* API Documentation may be accessed here: [ReadtheDocs](https://sprit.readthedocs.io/en/latest/sprit.html) and [Github Pages](https://rjbalikian.github.io/SPRIT-HVSR/main.html)
* The Wiki and Tutorials may be accessed here: [https://github.com/RJbalikian/SPRIT-HVSR/wiki](https://github.com/RJbalikian/SPRIT-HVSR/wiki)
* Source Code may be accessed here: [https://github.com/RJbalikian/SPRIT-HVSR](https://github.com/RJbalikian/SPRIT-HVSR)
* PyPI repository may be accessed here: [https://pypi.org/project/sprit/](https://pypi.org/project/sprit/)
"""
if spritLogoPath.exists():
#encodedImage =
mainContainerInitText = mainContainerInitText.replace('sprit_logo', f"<img src='data:image/png;base64,{base64.b64encode(spritLogoPath.read_bytes()).decode()}' class='img-fluid'>")
else:
mainContainerInitText = mainContainerInitText.replace('sprit_logo', "")
st.markdown(mainContainerInitText, unsafe_allow_html=True)
licenseExpander = st.expander(label='License information')
licenseInfo = """
## MIT License
SpRIT is licensed under the MIT License:
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE."""
licenseExpander.markdown(licenseInfo, unsafe_allow_html=True)
versionText = "### This app is using SpRIT v0.0.0"
versionText = versionText.replace('0.0.0', spritversion)
st.markdown(versionText)
if VERBOSE:
print('Start sig loop, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
st.session_state.session_log = f'\n ## Application started at {datetime.datetime.now()}\n\n'
# Get all function defaults
for fun, funDict in funList:
funSig = inspect.signature(fun)
for arg in funSig.parameters.keys():
if not (funSig.parameters[arg].default is funSig.parameters[arg].empty):
funDict[arg] = funSig.parameters[arg].default
run_kwargs[arg] = funSig.parameters[arg].default
gpsd_kwargs['ppsd_length'] = run_kwargs['ppsd_length'] = 30
gpsd_kwargs['skip_on_gaps'] = run_kwargs['skip_on_gaps'] = True
gpsd_kwargs['period_step_octaves'] = run_kwargs['period_step_octaves'] = 0.03125
gpsd_kwargs['period_limits'] = run_kwargs['period_limits'] = [1/run_kwargs['hvsr_band'][1], 1/run_kwargs['hvsr_band'][0]]
if VERBOSE:
print('Done getting kwargs: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
print('Setting up session state: ', len(st.session_state.keys()))
#st.session_state["updated_kwargs"] = {}
for key, value in run_kwargs.items():
if VERBOSE:
print('resetting')
print_param(PARAM2PRINT)
print(key, ": ", value)
# if key in st.session_state.keys() and (st.session_state[key] != value):
st.session_state[key] = value
#listItems = ['source', 'tzone', 'elev_unit', 'data_export_format', 'detrend', 'special_handling', 'peak_selection', 'freq_smooth', 'horizontal_method', 'stalta_thresh']
## Convert items to lists
#for arg, value in st.session_state.items():
# if arg in listItems:
# valList = [value]
# st.session_state[arg] = valList
# run_kwargs[arg] = st.session_state[arg]
# Convert lists and numbers to strings
strItems = ['channels', 'xcoord', 'ycoord', 'elevation', 'detrend_options', 'filter_options', 'horizontal_method']
for arg, value in st.session_state.items():
if arg in strItems:
if isinstance(value, (list, tuple)):
newVal = '['
for item in value:
newVal = newVal+item+', '
newVal = newVal[:-2]+']'
st.session_state[arg] = newVal
run_kwargs[arg] = newVal
else:
st.session_state[arg] = str(value)
run_kwargs[arg] = str(value)
if VERBOSE:
print_param(PARAM2PRINT)
# Convert all times to python datetime objects
dtimeItems=['acq_date', 'starttime', 'endtime']
for arg , value in st.session_state.items():
if arg in dtimeItems:
if isinstance(value, str):
st.session_state[arg] = datetime.datetime.strptime(value, "%Y-%m-%d")
run_kwargs[arg] = datetime.datetime.strptime(value, "%Y-%m-%d")
elif isinstance(st.session_state[arg], UTCDateTime):
st.session_state[arg] = value.datetime
run_kwargs[arg] = value.datetime
else:
st.session_state[arg] = value
run_kwargs[arg] = value
if VERBOSE:
print_param(PARAM2PRINT)
# Case matching
st.session_state.outlier_method = run_kwargs['outlier_method'] = st.session_state.outlier_method.title()
st.session_state.data_export_format = run_kwargs['data_export_format'] = st.session_state.data_export_format.upper()
st.session_state.detrend = run_kwargs['detrend'] = st.session_state.detrend.title()
st.session_state.azimuth_type = run_kwargs['azimuth_type'] = st.session_state.azimuth_type.title()
st.session_state.peak_selection = run_kwargs['peak_selection'] = st.session_state.peak_selection.title()
st.session_state.freq_smooth = run_kwargs['freq_smooth'] = st.session_state.freq_smooth.title()
st.session_state.source = run_kwargs['source'] = st.session_state.source.title()
st.session_state.plot_engine = run_kwargs['plot_engine'] = st.session_state.plot_engine.title()
# Update Nones
# Remove method
if st.session_state.remove_method is None:
st.session_state.remove_method = run_kwargs['remove_method'] = 'None'
else:
st.session_state.remove_method = run_kwargs['remove_method'] = st.session_state.remove_method.title()
# Other updates for streamlit specifically
st.session_state.azimuth_unit = run_kwargs['azimuth_unit'] = '°'
st.session_state.plot_engine = run_kwargs['plot_engine'] = "Matplotlib"
# Horizontal_method
methodDict = {'0':'Diffuse Field Assumption', '1':'Arithmetic Mean', '2':'Geometric Mean', '3':'Vector Summation', '4':'Quadratic Mean', '5':'Maximum Horizontal Value', '6':'Azimuth', "None":"Vector Summation"}
if st.session_state.horizontal_method is None:
st.session_state.horizontal_method = run_kwargs['horizontal_method'] = methodDict["3"]
else:
st.session_state.horizontal_method = run_kwargs['horizontal_method'] = methodDict[st.session_state.horizontal_method]
# Set Defaults
st.session_state.default_params = run_kwargs
if VERBOSE:
print_param(PARAM2PRINT)
st.session_state.run_kws = list(run_kwargs.keys())
if VERBOSE:
for key, value in st.session_state.items():
print("session st: ", st.session_state[key], type( st.session_state[key]), '| rkwargs:', value, type(value))
if VERBOSE:
print('Done with setup, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
st.session_state['NewSessionState'] = copy.copy(st.session_state)
def update_session_log(message=''):
if not hasattr(st.session_state, 'session_log'):
st.session_state.session_log='# Log'
newLogEntry = f'\n[{datetime.datetime.now()}] '
if message == '':
newLogEntry += '<action taken. message not specified>'
else:
newLogEntry += message
newLogEntry += '\n'
st.session_state.session_log += newLogEntry
def show_logs():
st.info(st.session_state.session_log, icon=':material/overview:')
def check_if_default():
if len(st.session_state.keys()) > 0:
print('Checking defaults, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
if VERBOSE:
check_if_default()
def text_change(VERBOSE=VERBOSE):
#Just a function to run so something is done when text changes
if VERBOSE:
print('TEXTCHange')
def on_file_upload():
file = st.session_state.datapath_uploader
temp_dir = tempfile.mkdtemp()
path = pathlib.Path(temp_dir).joinpath(file.name)
with open(path, "wb") as f:
f.write(file.getvalue())
if VERBOSE:
print(file.name)
st.session_state.input_data = path.as_posix()
update_session_log(f'File uploaded: {file}\n\t Stored at {path.as_posix})')
# Set up main container
def setup_main_container(do_setup_tabs=False):
mainContainer = st.container()
st.session_state.mainContainer = mainContainer
if do_setup_tabs:
setup_tabs(mainContainer)
st.session_state.mainContain_setup = True
if not st.session_state.initial_setup:
setup_main_container(do_setup_tabs=False)
# Set up tabs
def setup_tabs(mainContainer):
resultsTab, inputTab, outlierTab, infoTab, logsTab = mainContainer.tabs(['Results', 'Data', 'Outliers', 'Info', 'Logs'])
plotReportTab, csvReportTab, strReportTab = resultsTab.tabs(['Summary/Plot', 'Results Table', 'Print Report'])
st.session_state.inputTab = inputTab
st.session_state.outlierTab = outlierTab
st.session_state.infoTab = infoTab
st.session_state.logTab = logsTab
st.session_state.resultsTab = resultsTab
st.session_state.plotReportTab = plotReportTab
st.session_state.csvReportTab = csvReportTab
st.session_state.strReportTab = strReportTab
st.session_state.tabs_setup = True
def on_run_data():
update_session_log("Started data processing")
# Runs sample data if nothing specified
if st.session_state.input_data == '':
st.session_state.input_data = 'sample'
# Now run the data
srun = {}
for key, value in st.session_state.items():
if key in st.session_state.run_kws:
if value != st.session_state.default_params[key]:
if str(value) != str(st.session_state.default_params[key]):
if isinstance(value, (tuple, list)):
if tuple(value) != tuple(st.session_state.default_params[key]):
srun[key] = value
print("ADDED", key, value)
else:
srun[key] = value
if key == 'plot_engine':
srun[key] = value
# Get plots all right
#srun['plot_engine'] = 'matplotlib'
srun['plot_input_stream'] = True
for rocK in inspect.signature(sprit_hvsr.remove_outlier_curves).parameters.keys():
if rocK in srun and rocK != 'plot_engine':
srun['show_outlier_plot'] = False
break
srun['show_plot'] = False
srun['verbose'] = False #True
# Update outputs
srun['report_export_format'] = None
srun['show_pdf_report'] = False
srun['show_print_report'] = True
srun['show_plot_report'] = False
if VERBOSE:
print('SPRIT RUN', srun)
st.toast('Data is processing', icon="⌛")
setup_main_container(do_setup_tabs=False)
with st.session_state.mainContainer:
spinnerText = '## Data is processing with default parameters.'
excludedKeys = ['plot_engine', 'plot_input_stream', 'show_plot', 'verbose', 'show_outlier_plot']
NOWTIME = datetime.datetime.now()
secondaryDefaults = {'acq_date': datetime.date(NOWTIME.year, NOWTIME.month, NOWTIME.day),
'hvsr_band':tuple(DEFAULT_BAND_LIST), 'use_hv_curves':True,
'starttime':datetime.time(0,0,0),
'endtime':datetime.time(23, 59, 0),
'peak_freq_range':tuple(DEFAULT_BAND_LIST),
'stalta_thresh':(8, 16),
'period_limits':(1/DEFAULT_BAND_LIST[1], 1/DEFAULT_BAND_LIST[0]),
'remove_method':['None'],
'report_export_format':None,
'report_formats': ['print', 'table', 'plot', 'html', 'pdf'] ,
'show_pdf_report':False,
'show_print_report':True,
'show_plot_report':False,
'elev_unit':'m',
'plot_type':'HVSR p ann C+ p ann Spec p',
'suppress_report_outputs':True
}
nonDefaultParams = False
srun['report_formats'] = ['print', 'table', 'plot', 'html', 'pdf']
srun['suppress_report_outputs'] = True
if 'input_data' in srun:
del srun['input_data']
# Display non-default parameters, if applicable
for key, value in srun.items():
if key not in excludedKeys:
if key in secondaryDefaults and secondaryDefaults[key] == value:
pass
else:
if nonDefaultParams is False:
spinnerDFList = []
nonDefaultParams = True
def _get_centered_text(text, just='center', add_parenth=False, size=20):
if len(str(text)) > size:
keyText = str(text)[:size-5]+ '...'
else:
keyText = str(text)
if add_parenth:
keyText = f"({keyText})"
if just=='center':
return keyText.center(size)
elif just=='right':
return keyText.rjust(size)
elif just=='left':
return keyText.ljust(size)
keyText = _get_centered_text(key)
valText = _get_centered_text(value, just='center')
valTypeText = _get_centered_text(type(value), just='left', add_parenth=True)
defValText = _get_centered_text(st.session_state.default_params[key], just='center')
defValTypeText = _get_centered_text(type(st.session_state.default_params[key]), just='left', add_parenth=True)
spinnerText = spinnerText + f"\n\t| {keyText} | {valText} {valTypeText} | {defValText} {defValTypeText} |"
spinnerDFList.append([key, value, type(value), st.session_state.default_params[key], type(st.session_state.default_params[key])])
if nonDefaultParams:
spinnerText = spinnerText.replace('default', 'the following non-default')
tableHeader = "\n\t| Parameter | Input Value (and type) | Default value (and type) |"
tableHeader2 = "\n\t|----------------------|-------------------------------------------|-----------------------------------------------|\n\t"
spinnerText = spinnerText.replace("\n\t", tableHeader+tableHeader2, 1)
spinnerDF = pd.DataFrame(spinnerDFList, columns=['Parameter', "Value Selected", "Selected value type", 'Default Value', 'Default value type'])
inputData = st.session_state.input_data
if hasattr(st.session_state, 'data_ingested') and st.session_state.data_ingested:
inputData = st.session_state.stream_edited
SRUNText = '\n\n -'.join([f"{str(k).ljust(25)} : {v}" for k, v in srun.items()])
update_session_log(f"Processing parameters:\n\n -{SRUNText}")
with st.spinner(spinnerText, show_time=True):
st.session_state.hvsr_data = sprit_hvsr.run(input_data=inputData, **srun)
update_session_log('Processing Complete')
st.balloons()
st.session_state.stream = st.session_state.hvsr_data['stream']
st.session_state.stream_edited = st.session_state.hvsr_data['stream_edited']
display_download_buttons()
st.toast('Displaying results (download available)')
display_results()
st.session_state.prev_datapath = st.session_state.input_data
update_session_log("Processed data displayed")
st.session_state.logTab.markdown(st.session_state.session_log)
def on_read_data():
update_session_log(f"Started data read: {st.session_state.input_data}")
if 'read_button' not in st.session_state.keys() or not st.session_state.read_button:
return
if st.session_state.input_data == '' or st.session_state.input_data is None:
st.session_state.input_data = 'sample'
st.session_state.mainContainer = st.container()
st.session_state.inputTab, st.session_state.infoTab, st.session_state.logTab = st.session_state.mainContainer.tabs(['Raw Seismic Data', 'Info', 'Logs'])
if st.session_state.input_data != '':
srun = {}
for key, value in st.session_state.items():
if key in st.session_state.run_kws:
if value != st.session_state.default_params[key]:
if str(value) != str(st.session_state.default_params[key]):
srun[key] = value
if key == 'plot_engine':
srun[key] = value
ipKwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.input_params).parameters.keys())}
fdKwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.fetch_data).parameters.keys())}
st.toast('Reading data', icon="⌛")
with st.spinner(f"Reading data: {ipKwargs['input_data']}"):
inParams = sprit_hvsr.input_params(**ipKwargs)
st.session_state.hvsr_data = sprit_hvsr.fetch_data(inParams, **fdKwargs)
st.session_state.stream = st.session_state.hvsr_data.stream
if hasattr(st.session_state.hvsr_data, 'stream_edited'):
st.session_state.stream_edited = st.session_state.hvsr_data.stream_edited
else:
st.session_state.stream_edited = st.session_state.hvsr_data.stream.copy()
update_session_log(f"Data ingested: \n{str(st.session_state.hvsr_data.stream).replace('Stream:', 'Stream:\n\n\t').replace('samples', 'samples\n\n\t')}")
display_read_data(do_setup_tabs=False)
st.session_state.data_ingested = True
update_session_log("Ingested data displayed")
st.session_state.logTab.markdown(st.session_state.session_log)
def do_interactive_display():
if st.session_state.interactive_display:
st.session_state.plot_engine = "Plotly"
update_session_log("Interactive plots activated (now using plotly)")
else:
st.session_state.plot_engine = "Matplotlib"
update_session_log("Interactive plots deactivated (now using matplotlib)")
def display_read_data(do_setup_tabs=False):
if do_setup_tabs:
st.session_state.mainContainer = st.container()
st.session_state.inputTab, st.session_state.infoTab = st.session_state.mainContainer.tabs(['Raw Seismic Data', 'Info'])
st.session_state.input_fig = make_input_fig_plotly()
if st.session_state.plot_engine == 'Matplotlib':
st.session_state.input_fig = make_input_fig_pyplot()
if not hasattr(st.session_state, 'data_plot'):
st.session_state.data_chart_event = st.session_state.inputTab.pyplot(st.session_state.input_fig, width='stretch')
st.session_state.data_plot = None
else:
st.session_state.data_chart_event = st.session_state.inputTab.plotly_chart(st.session_state.input_fig,
on_select=update_data, key='data_plot',
selection_mode='box', width='stretch', theme='streamlit')
st.session_state.inputTab.write("Select any time window with the Box Selector (see the top right of chart) to remove it from analysis.")
#st.session_state.input_selection_mode = st.session_state.inputTab.pills('Window Selection Mode', options=['Add', "Delete"], key='input_selection_toggle',
# default='Add', on_change=update_selection_type, disabled=True,
# help='If in "Add" mode, windows for removal will be added at your selection. If "Delete" mode, these windows will be deleted. Currently only "Add" supported')
# Print information about the data to Info tab
st.session_state.infoTab.header("Information About Input Data")
st.session_state.infoTab.write(f"Acquisition Date: {st.session_state.hvsr_data['acq_date']}")
recLength = (UTCDateTime(st.session_state.hvsr_data['stream'][0].stats.endtime) - UTCDateTime(st.session_state.hvsr_data['stream'][0].stats.starttime))
st.session_state.infoTab.write(f"Record Length: {recLength/60:.2f} minutes ({recLength} seconds)")
st.session_state.infoTab.write("---")
st.session_state.infoTab.code(str(st.session_state.hvsr_data))
def display_buttons_and_results():
display_download_buttons()
display_results()
def display_results():
# Set up container for output data
setup_main_container(do_setup_tabs=True)
st.toast('Displaying results')
if st.session_state.interactive_display:
st.session_state.plot_engine = "Plotly"
if st.session_state.plot_engine == "Plotly":
# Print main results right away if taking time to plot others
if st.session_state.interactive_display:
st.session_state.mainContainer.code(body=st.session_state.hvsr_data['Print_Report'],
language='text')
st.session_state.mainContainer.dataframe(data=st.session_state.hvsr_data['Table_Report'])
# Input data
if st.session_state.interactive_display or (hasattr(st.session_state, 'data_results_toggle') and st.session_state.data_results_toggle):
st.session_state.input_fig = make_input_fig_plotly()
st.session_state.data_chart_event = st.session_state.inputTab.plotly_chart(st.session_state.input_fig,
on_select=update_data, key='data_plot',
selection_mode='box', width='stretch', theme='streamlit')
st.session_state.inputTab.write("Select any time window with the Box Selector (see the top right of chart) to remove it from analysis.")
#st.session_state.input_selection_mode = st.session_state.inputTab.pills('Window Selection Mode', options=['Add', "Delete"], key='input_selection_toggle',
# default='Add', on_change=update_selection_type, disabled=True,
# help='If in "Add" mode, windows for removal will be added at your selection. If "Delete" mode, these windows will be deleted. Currently only "Add" supported')
else:
st.session_state.inputTab.toggle(label='Display input data stream and windows used',
value=False,
on_change=display_buttons_and_results,
help='Toggle on to display interactive chart with input data stream for selecting windows for removal.',
key='data_results_toggle')
write_to_info_tab(st.session_state.infoTab)
if st.session_state.interactive_display or (hasattr(st.session_state, 'outlier_toggle') and st.session_state.outlier_toggle):
outlier_plot_in_tab()
else:
st.session_state.outlierTab.toggle(label='Display outlier chart',
value=False,
help='Turn on to display outlier chart (you may need to navigate back to this tab)',
key='outlier_toggle',
on_change=display_buttons_and_results)
if st.session_state.interactive_display:
st.session_state.plotReportTab.plotly_chart(st.session_state.hvsr_data['Plot_Report'], width='stretch')
else:
st.session_state.plotReportTab.html(st.session_state.hvsr_data["HTML_Report"])
st.session_state.csvReportTab.dataframe(data=st.session_state.hvsr_data['Table_Report'])
st.session_state.strReportTab.code(st.session_state.hvsr_data['Print_Report'], language=None)
else: # Matplotlib
# Input plot
st.session_state.input_fig = make_input_fig_pyplot()
st.session_state.data_chart_event = st.session_state.inputTab.pyplot(st.session_state.input_fig,
width='stretch')
# Info tab
write_to_info_tab(st.session_state.infoTab)
# Outlier chart
outlier_plot_in_tab()
if st.session_state.interactive_display:
st.session_state.plotReportTab.pyplot(st.session_state.hvsr_data['Plot_Report'], width='stretch')
else:
st.session_state.plotReportTab.html(st.session_state.hvsr_data["HTML_Report"])
#st.session_state.plotReportTab.pyplot(st.session_state.hvsr_data['Plot_Report'], width='stretch')
st.session_state.csvReportTab.dataframe(data=st.session_state.hvsr_data['Table_Report'])
st.session_state.strReportTab.code(st.session_state.hvsr_data['Print_Report'], language=None)
@st.fragment
def display_download_buttons():
##dlText, dlPDFReport, dlStream, dlTable, dlPlot, dlHVSR = st.session_state.mainContainer.columns([0.2, 0.16, 0.16, 0.16, 0.16, 0.16])
dlText, dlStream, dlHVSR, dlPDFReport, dlTable, dlPlot = st.columns([0.2, 0.16, 0.16, 0.16, 0.16, 0.16])
st.divider()
# Download Buttons
##st.session_state.dlText.text("Download Results: ")
dlText.text("Download Results: ")
# Set up variables for download section
hvData = st.session_state.hvsr_data
hvID = ''
if hasattr(hvData, 'hvsr_id'):
hvID = hvData['hvsr_id']
nowTimeStr = datetime.datetime.now().strftime("%Y-%m-%d")
# PDF Report download
#@st.cache_data
def _convert_pdf_for_download(_hv_data):
pdfPath = sprit_hvsr._generate_pdf_report(_hv_data, return_pdf_path=True)
with open(pdfPath, "rb") as pdf_file:
PDFbyte = pdf_file.read()
return PDFbyte
pdf_byte = _convert_pdf_for_download(hvData)
dlPDFReport.download_button(label="Report (.pdf)",
data=pdf_byte,
#on_click=display_results,
file_name=f"{hvData.site}_Report_{hvID}_{nowTimeStr}.pdf",
mime='application/octet-stream',
icon=":material/summarize:")
# Data Stream
#@st.cache_data
def _convert_stream_for_download(_stream):
strm = io.BytesIO()
_stream = _stream.split()
_stream.write(strm, format='MSEED')
return strm.getvalue()
streamBytes = _convert_stream_for_download(hvData.stream)
##st.session_state.dlStream.download_button(
dlStream.download_button(
label='Data (.mseed)',
data=streamBytes,
#on_click=display_results,
file_name=f"{hvData.site}_Stream_{hvID}_{nowTimeStr}.mseed",
icon=":material/graphic_eq:"
)
# Table download
#@st.cache_data
def _convert_table_for_download(df):
return df.to_csv().encode("utf-8")
csv = _convert_table_for_download(st.session_state.hvsr_data['Table_Report'])
##st.session_state.dlTable.download_button(
dlTable.download_button(
label="Table (.csv)",
data=csv,
file_name=f"{hvData.site}_TableReport_{hvID}_{nowTimeStr}.csv",
#on_click=display_results,
mime="text/csv",
icon=":material/table:",
)
# Plot
#@st.cache_data
def _convert_plot_for_download(_HV_Plot):
_img = io.BytesIO()
if st.session_state.plot_engine == 'Matplotlib':
_HV_Plot.savefig(_img, format='png')
else:
_img = _HV_Plot.to_image(format='png')
return _img
img = _convert_plot_for_download(hvData['Plot_Report'])
##st.session_state.dlPlot.download_button(
dlPlot.download_button(
label="Plot (.png)",
data=img,
file_name=f"{hvData.site}_HV-Plot_{hvID}_{nowTimeStr}.png",
mime="image/png",
#on_click=display_results,
icon=":material/analytics:"
)
# HVSR File
try:
#@st.cache_data
def _convert_hvsr_for_download(_hvsr_data):
hvData = copy.deepcopy(_hvsr_data)
for pk in sprit_hvsr.PLOT_KEYS:
if hasattr(hvData, pk):
delattr(hvData, pk)
_hvsrPickle = pickle.dumps(hvData)
return _hvsrPickle
hvsrPickle = _convert_hvsr_for_download(st.session_state.hvsr_data)
##st.session_state.dlHVSR.download_button(
dlHVSR.download_button(
label="Pickled (.hvsr)",
data=hvsrPickle,
file_name=f"{hvData.site}_HVSRData_{hvID}_{nowTimeStr}_pickled_app.hvsr",
#on_click=display_results,
mime='application/octet-stream',
icon=":material/database:")
except Exception as e:
print(e)
##st.session_state.dlHVSR.button(
dlHVSR.download_button(
label=".hvsr not available",
data='HVSR Data ',
disabled=True,
icon=":material/database:")
def on_reset():
st.toast("Session state cleared")
st.session_state = st.session_state['NewSessionState']
update_session_log("Session state cleared")
def _get_use_array(hvsr_data, f=None, timeWindowArr=None, psdArr=None):
streamEdit = st.session_state.stream_edited.copy()
earliestStart = UTCDateTime(3000, 12, 31)
for trace in streamEdit:
if trace.stats.starttime < earliestStart:
earliestStart = trace.stats.starttime
zList = []
eList = []
nList = []
streamEdit = streamEdit.split()
for trace in streamEdit:
traceSTime = trace.stats.starttime
traceETime = trace.stats.endtime
if trace.stats.component == 'Z':
zList.append([traceSTime, traceETime])
if trace.stats.component == 'E':
eList.append([traceSTime, traceETime])
if trace.stats.component == 'N':
nList.append([traceSTime, traceETime])
gapListUTC = []
for i, currWindow in enumerate(zList):
if i > 0:
prevWindow = zList[i-1]
gapListUTC.append([prevWindow[1], currWindow[0]])
gapList = [[np.datetime64(gTimeUTC.datetime) for gTimeUTC in gap] for gap in gapListUTC]
if hasattr(hvsr_data, 'hvsr_windows_df'):
hvdf = hvsr_data.hvsr_windows_df
tps = pd.Series(hvdf.index.copy(), name='TimesProcessed_Start', index=hvdf.index)
hvdf["TimesProcessed_Start"] = tps
useArrShape = np.array(f).shape[0]
else:
useSeriesList = []
sTimeSeriesList = []
eTimeSeriesList = []
useArrShape = 0
for i, tArr in enumerate(timeWindowArr):
useSeriesList.extend([True]*(np.array(tArr).shape[0]-1))
sTimeSeriesList.extend(tArr[:-1])
eTimeSeriesList.extend(tArr[1:])
useArrShape += np.array(psdArr[i]).shape[0]
useSeries = pd.Series(useSeriesList, name='Use')
sTimeSeries = pd.Series(sTimeSeriesList, name='TimesProcessed')
eTimeSeries = pd.Series(eTimeSeriesList, name='TimesProcessed_End')
hvdf = pd.DataFrame({'TimesProcessed':sTimeSeries,
'TimesProcessed_End':eTimeSeries,
'Use':useSeries})
hvdf.set_index('TimesProcessed', inplace=True, drop=True)
hvdf['TimesProcessed_Start'] = sTimeSeriesList
if 'TimesProcessed_Obspy' not in hvdf.columns:
hvdf['TimesProcessed_Obspy'] = [UTCDateTime(dt64) for dt64 in sTimeSeries]
hvdf['TimesProcessed_ObspyEnd'] = [UTCDateTime(dt64) for dt64 in eTimeSeries]
# Do processing
if len(gapListUTC) > 0:
for gap in gapListUTC:
stOutEndIn = hvdf['TimesProcessed_Obspy'].gt(gap[0]) & hvdf['TimesProcessed_Obspy'].lt(gap[1])
stInEndOut = hvdf['TimesProcessed_ObspyEnd'].gt(gap[0]) & hvdf['TimesProcessed_ObspyEnd'].lt(gap[1])
bothIn = hvdf['TimesProcessed_Obspy'].lt(gap[0]) & hvdf['TimesProcessed_ObspyEnd'].gt(gap[1])
bothOut = hvdf['TimesProcessed_Obspy'].gt(gap[0]) & hvdf['TimesProcessed_ObspyEnd'].lt(gap[1])
hvdf.loc[hvdf[stOutEndIn | stInEndOut | bothIn | bothOut].index, 'Use'] = False
return hvdf, useArrShape
@st.cache_data
def _generate_stream_specgram(_trace):
return signal.spectrogram(x=_trace.data,
fs=_trace.stats.sampling_rate,
mode='magnitude')
def make_input_fig_pyplot():
hvsr_data = st.session_state.hvsr_data
stream = hvsr_data.stream
inputFig = sprit_plot._plot_input_stream_mpl(stream=stream,
hv_data=hvsr_data,
return_fig=True)
st.session_state.input_fig = inputFig
st.session_state.hvsr_data.Input_Plot = inputFig
return inputFig
def make_input_fig_plotly():
no_subplots = 5
inputFig = make_subplots(rows=no_subplots, cols=1,
row_heights=[0.5, 0.02, 0.16, 0.16, 0.16],
shared_xaxes=True,
horizontal_spacing=0.01,
vertical_spacing=0.01
)
hvsr_data = st.session_state.hvsr_data
# Windows PSD and Used
#psdArr = np.flip(hvsr_data.psds["Z"]['psd_values'].T)
zStream = st.session_state.stream.select(component='Z').merge()
zTraces = zStream.split()
zTrace = zStream[0]
eStream = st.session_state.stream.select(component='E').merge()
eTraces = eStream.split()
eTrace = eStream[0]
nStream = st.session_state.stream.select(component='N').merge()
nTraces = nStream.split()
nTrace = nStream[0]
specKey = 'Z'
xTraceTimesAppZ = []
f = []
specTimes = []
psdArr = []
timeWindowArr = []
sTimeZ = zTraces[0].stats.starttime
sTimeE = eTraces[0].stats.starttime
sTimeN = nTraces[0].stats.starttime
# E
for i, eTr in enumerate(eTraces):
if i == 0:
xTraceTimesE = [np.datetime64((sTimeE + tT).datetime) for tT in eTr.times()]
xTraceTimesAppE = [[np.datetime64((sTimeE + tT).datetime) for tT in eTr.times()]]
else:
xTraceTimesAppE.append([np.datetime64((sTimeE + tT).datetime) for tT in eTr.times()])
xTraceTimesE.extend([np.datetime64((sTimeE + tT).datetime) for tT in eTr.times()])
# N
for i, nTr in enumerate(nTraces):
if i == 0:
xTraceTimesN = [np.datetime64((sTimeN + tT).datetime) for tT in nTr.times()]
xTraceTimesAppN = [[np.datetime64((sTimeN + tT).datetime) for tT in nTr.times()]]
else:
xTraceTimesAppN.append([np.datetime64((sTimeN + tT).datetime) for tT in nTr.times()])
xTraceTimesN.extend([np.datetime64((sTimeN + tT).datetime) for tT in nTr.times()])
for i, zTrace in enumerate(zTraces):
sTimeZ = zTrace.stats.starttime
if i == 0:
xTraceTimesZ = [np.datetime64((sTimeZ + tT).datetime) for tT in zTrace.times()]
xTraceTimesAppZ = [[np.datetime64((sTimeZ + tT).datetime) for tT in zTrace.times()]]
else:
xTraceTimesAppZ.append([np.datetime64((sTimeZ + tT).datetime) for tT in zTrace.times()])
xTraceTimesZ.extend([np.datetime64((sTimeZ + tT).datetime) for tT in zTrace.times()])
fTemp, specTimesTemp, psdArrTemp = _generate_stream_specgram(_trace=zTrace)
if fTemp[0] == 0:
fTemp[0] = fTemp[1]/10 # Fix so bottom number is not 0
f.append(fTemp)
specTimesTemp = list(specTimesTemp)
specTimesTemp.insert(0, 0)
specTimes.append(specTimesTemp)
timeWindowArr.append(np.array([np.datetime64((sTimeZ + tT).datetime) for tT in specTimesTemp]))
psdArr.append(psdArrTemp)
minz = np.percentile(psdArrTemp, 1)
maxz = np.percentile(psdArrTemp, 99)
hmap = goHeatmap(z=psdArrTemp,
x=timeWindowArr[i][:-1],
y=fTemp,
colorscale='Turbo', #opacity=0.8,
showlegend=False,
hovertemplate='Time [UTC]: %{x}<br>Frequency [Hz]: %{y:.2f}<br>Spectrogram Magnitude: %{z:.2f}<extra></extra>',
zmin=minz, zmax=maxz, showscale=False, name=f'{specKey} Component Spectrogram; Trace {i}')
inputFig.add_trace(hmap, row=1, col=1)
st.session_state.stream_spec_freqs = f
st.session_state.stream_spec_times = specTimes
st.session_state.psdArr = psdArr
hvsrBand = hvsr_data['hvsr_band']
inputFig.update_yaxes(type='log', range=[np.log10(hvsrBand[0]), np.log10(hvsrBand[-1])], row=1, col=1)
inputFig.update_yaxes(title={'text':f'Spectrogram ({specKey})'}, row=1, col=1)
# Get Use Array and hvdf
hvdf, useArrShape = _get_use_array(hvsr_data, f=f, timeWindowArr=timeWindowArr, psdArr=psdArr)
timelineFig = pxTimeline(data_frame=hvdf,
x_start='TimesProcessed_Start',
x_end='TimesProcessed_End',
y=['Used']*hvdf.shape[0],
#y="Use",#range_y=[-20, -10],
color='Use',
color_discrete_map={True: 'rgba(0,255,0,1)',
False: 'rgba(255,0,0,1)'})
for timelineTrace in timelineFig.data:
inputFig.add_trace(timelineTrace, row=2, col=1)
useArr = np.tile(hvdf.Use, (useArrShape, 1))
useArr = np.where(useArr == True, np.ones_like(useArr), np.zeros_like(useArr)).astype(int)
specOverlay = goHeatmap(z=useArr,
x=hvdf['TimesProcessed_Start'],
y=f,
colorscale=[[0, 'rgba(0,0,0,0.8)'], [0.1, 'rgba(255,255,255, 0.00001)'], [1, 'rgba(255,255,255, 0.00001)']],
showlegend=False,
#hovertemplate='Time [UTC]: %{x}<br>Frequency [Hz]: %{y:.2f}<br>Spectrogram Magnitude: %{z:.2f}<extra></extra>',
showscale=False, name=f'{specKey} Component Spectrogram')
inputFig.add_trace(specOverlay, row=1, col=1)
minTraceData = min(min(zTrace.data), min(eTrace.data), min(nTrace.data))
maxTraceData = max(max(zTrace.data), max(eTrace.data), max(nTrace.data))
streamOverlay = goHeatmap(z=useArr,
x=hvdf['TimesProcessed_Start'],
y=np.linspace(minTraceData, maxTraceData, useArr.shape[0]),
colorscale=[[0, 'rgba(0,0,0,0.8)'], [0.1, 'rgba(255,255,255, 0.00001)'], [1, 'rgba(255,255,255, 0.00001)']],
showlegend=False,
#hovertemplate='Time [UTC]: %{x}<br>Frequency [Hz]: %{y:.2f}<br>Spectrogram Magnitude: %{z:.2f}<extra></extra>',
showscale=False, name=f'{specKey} Component Spectrogram')
inputFig.add_trace(streamOverlay, row=3, col=1)
inputFig.add_trace(streamOverlay, row=4, col=1)
inputFig.add_trace(streamOverlay, row=5, col=1)
inputFig.update_yaxes(type='log', range=[np.log10(hvsrBand[0]), np.log10(hvsrBand[-1])], row=1, col=1)
inputFig.update_yaxes(title={'text':f'Spectrogram ({specKey})'}, row=1, col=1)
# Data traces
# Z Traces
for i, zTr in enumerate(zTraces):
if i == 0:
zDataFig = pxScatter(x=xTraceTimesAppZ[i], y=zTr.data)
else:
zTempFig = pxScatter(x=xTraceTimesAppZ[i], y=zTr.data)
for zFigTrace in zTempFig.data:
zDataFig.add_trace(zFigTrace)
zDataFig.update_traces(mode='markers+lines',
marker=dict(size=1, color='rgba(0,0,0,1)'),
line=dict(width=1, color='rgba(0,0,0,1)'),
selector=dict(mode='markers'))
for zTrace in zDataFig.data:
inputFig.add_trace(zTrace, row=3, col=1)
# E Traces
for i, eTr in enumerate(eTraces):
if i == 0:
eDataFig = pxScatter(x=xTraceTimesAppE[i], y=eTr.data)
else:
eTempFig = pxScatter(x=xTraceTimesAppE[i], y=eTr.data)
for eFigTrace in eTempFig.data:
eDataFig.add_trace(eFigTrace)
#eDataFig = pxScatter(x=xTraceTimes, y=eTrace.data)
eDataFig.update_traces(mode='markers+lines',
marker=dict(size=1, color='rgba(0,0,255,1)'),
line=dict(width=1, color='rgba(0,0,255,1)'),
selector=dict(mode='markers'))
for eTrace in eDataFig.data:
inputFig.add_trace(eTrace, row=4, col=1)
# N Traces
for i, nTr in enumerate(nTraces):
if i == 0:
nDataFig = pxScatter(x=xTraceTimesAppN[i], y=nTr.data)
else:
nTempFig = pxScatter(x=xTraceTimesAppN[i], y=nTr.data)
for nFigTrace in nTempFig.data:
nDataFig.add_trace(nFigTrace)
#nDataFig = pxScatter(x=xTraceTimes, y=nTrace.data)
nDataFig.update_traces(mode='markers+lines',
marker=dict(size=1, color='rgba(255,0,0,1)'),
line=dict(width=1, color='rgba(255,0,0,1)'),
selector=dict(mode='markers'))
for nTrace in nDataFig.data:
inputFig.add_trace(nTrace, row=5, col=1)
#zDataFig = pxScatter(x=xTraceTimes, y=zTrace.data)
#zDataFig.update_traces(mode='markers+lines',
# marker=dict(size=1, color='rgba(0,0,0,1)'),
# line=dict(width=1, color='rgba(0,0,0,1)'),
# selector=dict(mode='markers'))
#for zTrace in zDataFig.data:
# inputFig.add_trace(zTrace, row=3, col=1)
#eDataFig = pxScatter(x=xTraceTimes, y=eTrace.data)
#eDataFig.update_traces(mode='markers+lines',
# marker=dict(size=1, color='rgba(0,0,255,1)'),
# line=dict(width=1, color='rgba(0,0,255,1)'),
# selector=dict(mode='markers'))
#for eTrace in eDataFig.data:
# inputFig.add_trace(eTrace, row=4, col=1)
#nDataFig = pxScatter(x=xTraceTimes, y=nTrace.data)
#nDataFig.update_traces(mode='markers+lines',
# marker=dict(size=1, color='rgba(255,0,0,1)'),
# line=dict(width=1, color='rgba(255,0,0,1)'),
# selector=dict(mode='markers'))
#for nTrace in nDataFig.data:
# inputFig.add_trace(nTrace, row=5, col=1)
#inputFig.update_yaxes(title='In Use', row=5, col=1)
#inputFig.update_xaxes(title='Time', row=5, col=1,
# dtick=1000*60,)
inputFig.update_layout(title_text="Frequency and Data values over time",
height=650, showlegend=False)
chartStartT = min(xTraceTimesZ[0], xTraceTimesE[0], xTraceTimesN[0])
chartEndT = max(xTraceTimesZ[-1], xTraceTimesE[-1], xTraceTimesN[-1])
inputFig.update_xaxes(type='date', range=[chartStartT, chartEndT])
st.session_state.input_fig = inputFig
st.session_state.hvsr_data.Input_Plot = inputFig
return inputFig
def update_from_data_selection():
st.toast("Updating H/V Curve statistics")
if 'PPSDStatus' in st.session_state.hvsr_data.processing_status and st.session_state.hvsr_data.processing_status['PPSDStatus']:
gpsd_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.generate_psds).parameters.keys()) and k != 'hvsr_data'}
st.session_state.hvsr_data = sprit_hvsr.generate_psds(hvsr_data=st.session_state.hvsr_data, **gpsd_kwargs)
prochvsr_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.process_hvsr).parameters.keys()) and k != 'hvsr_data'}
checkPeaks_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.check_peaks).parameters.keys()) and k != 'hvsr_data'}
getRep_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.get_report).parameters.keys()) and k != 'hvsr_data'}
st.session_state.hvsr_data = sprit_hvsr.process_hvsr(hvsr_data=st.session_state.hvsr_data, **prochvsr_kwargs)
st.session_state.hvsr_data = sprit_hvsr.check_peaks(hvsr_data=st.session_state.hvsr_data, **checkPeaks_kwargs)
st.session_state.hvsr_data = sprit_hvsr.get_report(hvsr_results=st.session_state.hvsr_data, **getRep_kwargs)
display_results()
def update_data():
update_session_log("Manually removed data times used for processing/analysis")
st.session_state.data_chart_event = st.session_state.data_plot
specKey = 'Z'
hvsrBand = st.session_state.hvsr_data.hvsr_band
# Still figuring stuff out
# This seems to work well at the moment
windows = []
if len(st.session_state.data_chart_event['selection']['box']) > 0:
esb = st.session_state.data_chart_event['selection']['box']
for b in esb:
if b['x'][0] > b['x'][1]:
windows.append((b['x'][1], b['x'][0]))
else:
windows.append((b['x'][0], b['x'][1]))
# Reset the variables
st.session_state.data_chart_event = {"selection":{"points":[],
"point_indices":[],
'box':[],
'lasso':[]}}
if 'x_windows_out' not in st.session_state.hvsr_data.keys():
st.session_state.hvsr_data['x_windows_out'] = []
# Convert times to obspy.UTCDateTime
utcdtWin = []
for currWin in windows:
currUTCWin = []
# Get
stream1 = st.session_state.stream_edited.copy()
stream2 = st.session_state.stream_edited.copy()
stream1 = stream1.merge()
stream2 = stream2.merge()
for pdtimestamp in currWin:
currUTCWin.append(UTCDateTime(pdtimestamp))
utcdtWin.append(currUTCWin)
st.session_state.hvsr_data['x_windows_out'].append(currUTCWin)
# Trim data with gap in the middle where we remvoed data
#if st.session_state.input_selection_mode == 'Add':
stream1.trim(starttime=stream1[0].stats.starttime, endtime=currUTCWin[0])
stream2.trim(starttime=currUTCWin[1], endtime=stream2[0].stats.endtime)
# Merge data back
newStream = (stream1 + stream2).merge()
st.session_state.hvsr_data['stream_edited'] = newStream
st.session_state.stream_edited = newStream
# Use edited data to update location of bars
# Update useArr
hvdf, useArrShape = _get_use_array(hvsr_data=st.session_state.hvsr_data,
f=st.session_state.stream_spec_freqs,
timeWindowArr=st.session_state.stream_spec_times,
psdArr=st.session_state.psdArr)
useArr = np.tile(hvdf.Use, (useArrShape, 1))
useArr = np.where(useArr == True, np.ones_like(useArr), np.zeros_like(useArr)).astype(int)
newSpecOverlay = goHeatmap(z = useArr,
x = hvdf['TimesProcessed_Start'],
y=st.session_state.stream_spec_freqs,
colorscale=[[0, 'rgba(0,0,0,0.8)'], [0.1, 'rgba(255,255,255, 0.00001)'], [1, 'rgba(255,255,255, 0.00001)']],
showlegend=False,
#hovertemplate='Time [UTC]: %{x}<br>Frequency [Hz]: %{y:.2f}<br>Spectrogram Magnitude: %{z:.2f}<extra></extra>',
showscale=False,
)
st.session_state.input_fig.add_trace(newSpecOverlay, row=1, col=1)
st.session_state.input_fig.update_yaxes(type='log', range=[np.log10(hvsrBand[0]), np.log10(hvsrBand[-1])], row=1, col=1)
st.session_state.input_fig.update_yaxes(title={'text':f'Spectrogram ({specKey})'}, row=1, col=1)
st.session_state.input_fig.update_layout(showlegend=False)
def has_attributes(obj, *attributes):
return all(hasattr(obj, attr) for attr in attributes)
procCond1 = st.session_state.hvsr_data['processing_status']['process_hvsr_status']
procCond2 = st.session_state.hvsr_data['processing_status']['overall_status']
procCond3 = has_attributes(st.session_state.hvsr_data, "Plot_Report", "Print_Report", "Table_Report")
readCond1 = st.session_state.hvsr_data['processing_status']['input_params_status']
readCond2 = st.session_state.hvsr_data['processing_status']['fetch_data_status']
if procCond1 and procCond2 and procCond3:
statusMsg = 'Excluding the following window'
if len(utcdtWin) != 1:
statusMsg += 's'
updateCol, statusCol = st.columns([0.2, 0.8])
with statusCol.status(statusMsg):
st.dataframe(pd.DataFrame(utcdtWin, columns=['Window Start Time (UTC)', 'Window End Time (UTC)']))
updateCol.button("Rerun results statistics",
on_click=update_from_data_selection,
type='primary', icon=":material/update:")
#display_results()
elif readCond1 and readCond2:
display_read_data(do_setup_tabs=True)
else:
# For dat that did not process correctly
st.session_state.mainContainer.warning('Data not read or processed correctly')
def update_selection_type():
#st.session_state.input_selection_mode = st.session_state.input_selection_toggle
pass
def write_to_info_tab(infoTab):
with infoTab:
st.markdown("# Processing Parameters Used")
hvsrDataList = ['params', 'hvsr_data', 'hvsr_results']
for fun, kwargDict in funList:
funSig = inspect.signature(fun)
# excludeKeys = ['params', 'hvsr_data', 'hvsr_results']
funMD = ""
for arg in funSig.parameters.keys():
if arg in st.session_state.keys() and arg not in hvsrDataList:
funMD = funMD + f"""\n * {arg} = {st.session_state[arg]}"""
with st.expander(f"{fun.__name__}"):
st.markdown(funMD, unsafe_allow_html=True)
def update_from_outlier_selection():
"""This is intended as a callback for updating the main results tab, etc. after updating outlier curves"""
st.toast("Updating H/V Curve statistics")
prochvsr_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.process_hvsr).parameters.keys()) and k != 'hvsr_data'}
checkPeaks_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.check_peaks).parameters.keys()) and k != 'hvsr_data'}
getRep_kwargs = {k: v for k, v in st.session_state.items() if k in tuple(inspect.signature(sprit_hvsr.get_report).parameters.keys()) and k != 'hvsr_data'}
st.session_state.hvsr_data = sprit_hvsr.process_hvsr(hvsr_data=st.session_state.hvsr_data, **prochvsr_kwargs)
st.session_state.hvsr_data = sprit_hvsr.check_peaks(hvsr_data=st.session_state.hvsr_data, **checkPeaks_kwargs)
st.session_state.hvsr_data = sprit_hvsr.get_report(hvsr_results=st.session_state.hvsr_data, **getRep_kwargs)
display_download_buttons()
display_results()
def update_outlier():
update_session_log("Manually selected curves to remove from H/V analysis")
hvDF = st.session_state.hvsr_data['hvsr_windows_df']
st.session_state.outlier_chart_event = st.session_state.outlier_plot
curves2Remove = np.unique([p['curve_number'] for p in st.session_state.outlier_chart_event['selection']['points']])
st.session_state.outlier_curves_to_remove = list(curves2Remove)
if len(st.session_state.outlier_curves_to_remove) > 0:
outlierMsgList = []
outlierMsgCols = ['Window Number', "Window Start Time"]
for remCurve in st.session_state.outlier_curves_to_remove:
currInd = hvDF.iloc[remCurve].name
outlierMsgList.append([remCurve, currInd])
hvDF.loc[currInd, "Use"] = False
statusMsg = "Removing specified outlier curve"
if len(st.session_state.outlier_curves_to_remove) != 1:
statusMsg += 's'
#st.toast(statusMsg)
updateCol, statusCol = st.columns([0.2, 0.8])
with statusCol.status(statusMsg):
st.dataframe(pd.DataFrame(outlierMsgList, columns=outlierMsgCols))
updateCol.button("Rerun results statistics", on_click=update_from_outlier_selection,
type='primary', icon=":material/update:")
display_results()
def outlier_plot_in_tab():
if st.session_state.plot_engine == 'Matplotlib':
outlierFig = sprit_plot.plot_outlier_curves(st.session_state.hvsr_data,
plot_engine='Matplotlib')
st.session_state.outlierTab.pyplot(outlierFig,
width='stretch')
st.session_state.outlier_plot = None
else:
hvDF = st.session_state.hvsr_data['hvsr_windows_df']
x_data = st.session_state.hvsr_data['x_freqs']['Z'][:-1]
no_subplots = 1
outlierFig = make_subplots(rows=no_subplots, cols=1,
shared_xaxes=True, horizontal_spacing=0.01,
vertical_spacing=0.1)
scatterFig = pxScatter()
scatter_traces = []
line_traces = []
for row, hvsr_data in enumerate(hvDF['HV_Curves']):
currInd = hvDF.iloc[row].name
if hvDF.loc[currInd, 'Use']:
scatterArray = np.array(list(hvsr_data)[::5])
x_data_Scatter = np.array(list(x_data)[::5])
currFig = pxScatter(x=x_data_Scatter, y=scatterArray)
currFig.update_traces(mode='markers+lines',
marker=dict(size=1, color='rgba(0,0,0,0.1)'),
line=dict(width=1, color='rgba(0,0,0,0.1)'),
selector=dict(mode='markers'))
scatter_traces.append(currFig)
else:
scatterArray = np.array(list(hvsr_data)[::5])
x_data_Scatter = np.array(list(x_data)[::5])
currFig = pxScatter(x=x_data_Scatter, y=scatterArray,
opacity=0.5)
currFig.update_traces(mode='markers+lines',
marker=dict(size=1, color='rgba(195,87,0,0.4)'),
line=dict(width=1, color='rgba(195,87,0,0.4)'),
selector=dict(mode='markers'))
scatter_traces.append(currFig)
# Add median line
medArr = np.nanmedian(np.stack(hvDF['HV_Curves'][hvDF['Use']]), axis=0)
scatterArray = np.array(list(medArr)[::10])
x_data_Scatter = np.array(list(x_data)[::10])
currFig = px.line(x=x_data_Scatter, y=scatterArray,
color_discrete_sequence=['red'])
currFig.update_traces(line=dict(width=3, color='black'))
scatter_traces.append(currFig)
for tr in scatter_traces:
for trace in tr.data:
outlierFig.add_traces(trace, rows=1, cols=1)
outlierFig.update_xaxes(title='Frequency [Hz]', type="log", row=1, col=1)
outlierFig.update_yaxes(title='H/V Ratio', row=1, col=1)
outlierFig.update_layout(title_text="H/V Curve Outlier Display & Selection")
st.session_state.outlierTab.write("Select any curve(s) with your cursor or the Box or Lasso Selectors (hover over the top right of chart) to remove from the statistics and analysis of results.")
# Output figure to correct tab
st.session_state.outlierTab.plotly_chart(outlierFig,
on_select=update_outlier,
key='outlier_plot',
width='stretch',
theme='streamlit')
def write_to_outlierTab():
pass
# Initial setup
setup_session_state()
# DEFINE SIDEBAR
if VERBOSE:
print('About to start setting up sidebar, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
# Set up sidebar
with st.sidebar:
if VERBOSE:
print('Start setting up sidebar, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
st.header('SpRIT HVSR', divider='rainbow')
inputDataCol, sourceCol = st.columns([0.7, 0.3])
datapathInput = inputDataCol.text_input("Input Data", key='input_data',
placeholder='Enter data filepath',
help="Enter a filepath to be read by obspy.read(). On the web app, a temporary copy of this file will be made.")
sourceCol.selectbox('Source', options=['File', 'Raw', 'Directory', "Batch"], index=0, key='source',
help='File: a single file for analysis. All the rest are experimental in the web app. Raw is used with Raspberry Shake data to read native file structure. Directory gets all relevant files in a directory. Batch is for loading a .csv file for batch analysis.')
with st.expander("Click to access data uploader"):
st.file_uploader("Upload data file(s)", type=OBSPYFORMATS, accept_multiple_files=False, key='datapath_uploader', on_change=on_file_upload)
bottom_container = st.container()
# Create top menu
with bottom_container:
resetCol, readCol, runCol = st.columns([0.3, 0.3, 0.4])
runLabel = 'Run'
readLabel = 'Read'
if st.session_state.input_data == '' or st.session_state.input_data is None:
runLabel = 'Demo Run'
readLabel = 'Demo Read'
resetCol.button('Reset', disabled=False, width='stretch',
on_click=on_reset, key='reset_button')
readCol.button(readLabel, disabled=False, width='stretch',
on_click=on_read_data, key='read_button')
runCol.button(runLabel, type='primary', width='stretch',
on_click=on_run_data, key='run_button')
if VERBOSE:
print('Done setting up bottom container, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
st.toggle(label='Display interactive charts (slower)', value=False, key='interactive_display',
on_change=do_interactive_display,
help="Whether to display interactive charts for the data, outliers, and results charts. Interactive charts take longer to display, but allow graphical editing of the data.")
st.header('Settings')
mainSettings = st.container()
with mainSettings:
siteNameCol, projCol = st.columns([0.5, 0.5])
siteNameCol.text_input("Site Name", placeholder='HVSR Site', on_change=text_change, key='site')
projCol.text_input("Project/County Name", on_change=text_change, key='project')
#instCol.text_input('Instrument', help='Raspberry Shake and Tromino are currently the only values with special treatment. If a filepath, can use a .inst instrument file (json format)', key='instrument')
stationCol, instCol = st.columns([0.5, 0.5])
stationCol.text_input("Station/Partition", placeholder='RAC84', key='station')
instCol.selectbox('Instrument', key='instrument',
options=['Seismometer', 'Raspberry Shake', 'Tromino Yellow', 'Tromino Blue'],
help='Some instruments require special inputs to read in the data correctly. If not one of the instruments listed, or if reading in an obspy-supported file directly, leave as "Seismometer"')
xCoordCol, yCoordCol, inCRSCol = st.columns([0.3333, 0.3333, 0.3333])
xCoordCol.text_input('X Coordinate', help='i.e., Longitude or Easting', key='xcoord')
yCoordCol.text_input('Y Coordinate', help='i.e., Latitude or Northing', key='ycoord')
inCRSCol.text_input('CRS', help='By default, "EPSG:4326". Can be EPSG code or anything accepted by pyproj.CRS.from_user_input()', key='input_crs')
zCoordCol, elevUnitCol, outCRSCol = st.columns([0.333, 0.333, 0.333])
zCoordCol.text_input('Z Coordinate', help='i.e., Elevation', key='elevation')
elevUnitCol.selectbox('Elev. (Z) Unit', options=['meters', 'feet'], key='elev_unit', help='i.e., Elevation unit')
outCRSCol.text_input('CRS for Export', help='Can be EPSG code or anything accepted by pyproj.CRS.from_user_input()', key='output_crs')
# Date/time settings
dateCol, sTimeCol, eTimeCol, tzoneCol = st.columns([0.3,0.25,0.25,0.2])
dateCol.date_input('Acquisition Date', format='YYYY-MM-DD', key='acq_date')
sTimeCol.time_input('Start time', step=60, key='starttime')
eTimeCol.time_input('End time', step=60, key='endtime')
tZoneList = list(zoneinfo.available_timezones())
tZoneList.sort()
tZoneList.insert(0, "localtime")
tZoneList.insert(0, "US/Pacific")
tZoneList.insert(0, "US/Mountain")
tZoneList.insert(0, "US/Eastern")
tZoneList.insert(0, "US/Central")
tZoneList.insert(0, "UTC")
tzoneCol.selectbox('Timezone', options=tZoneList, key='tzone')
# Processing limits
pfrDef = tuple(inspect.signature(sprit_hvsr.input_params).parameters['peak_freq_range'].default)
st.session_state.peak_freq_range = st.select_slider('Peak Frequency Range', options=bandVals,
value=pfrDef,
#key='peak_freq_range'
)
st.session_state.hvsr_band = st.select_slider('HVSR Band', options=bandVals, #key='hvsr_band',
value=st.session_state.hvsr_band
)
if VERBOSE:
print_param(PARAM2PRINT)
st.header('Additional Settings', divider='gray')
with st.expander('Expand to modify additional settings'):
if VERBOSE:
print('Setting up sidebar expander, session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
ipSetTab, fdSetTab, azSetTab, rmnocSetTab, gpSetTab, phvsrSetTab, plotSetTab = st.tabs(['Instrument', 'Data', "Azimuths", "Noise", 'PPSDs', 'H/V', 'Plot'])
#@st.experimental_dialog("Update Input Parameters", width='large')
#def open_ip_dialog():
with ipSetTab:
if VERBOSE:
print('Setting up input tab, session state length: ', len(st.session_state.keys()))
#with st.expander('Instrument settings'):
st.text_input("Network", placeholder='AM', key='network')
st.text_input("Location", placeholder='00', key='loc')
st.text_input("Channels", placeholder='EHZ, EHE, EHN', key='channels')
st.text_input('Metadata Filepath', help='Filepath to instrument response file', key='metadata')
#with st.expander('Primary Input Parameters', expanded=True):
#if "hvsr_band" not in st.session_state:
# st.session_state.hvsr_band = [0.4, 40]
#@st.experimental_dialog("Update Parameters to Fetch Data", width='large')
#def open_fd_dialog():
with fdSetTab:
if VERBOSE:
print('Setting up fd tab, session state length: ', len(st.session_state.keys()))
#source: str = 'file',
st.text_input('Data export directory', help='Directory for exporting raw/trimmed data', key='data_export_dir')
st.selectbox('Data export format', options=OBSPYFORMATS, index=11, key='data_export_format')
# Detrending options
st.selectbox('Detrend Type', options=['None', 'Simple', 'Linear', 'Constant/Demean', 'Polynomial', 'Spline'], index=5,
help='Detrend Type use by `type` parameter of obspy.trace.Trace.detrend()', key='detrend')
st.text_input('Detrend options', value='order=2',
help="Value(s) to pass to the **options argument of obspy.trace.Trace.detrend()",
key='detrend_options')
# Filter options
st.selectbox('Filter Type', options=['None', 'bandpass', 'bandstop',
'lowpass', 'highpass',
'lowpass_cheby_2', 'lowpass_fir', 'remez_fir'],
index=0,
help='Detrend Type use by `type` parameter of obspy.trace.Trace.filter()', key='filter')
st.text_input('Filter options',
help="Value(s) to pass to the **options argument of obspy.trace.Trace.filter()",
key='filter_options')
if VERBOSE:
print_param(PARAM2PRINT)
with azSetTab:
if VERBOSE:
print('Setting up az tab, session state length: ', len(st.session_state.keys()))
st.toggle("Calculate Azimuths", value=False,
help='Whether to calculate azimuths for data.',
key='azimuth_calculation')
az_disabled = True
if hasattr(st.session_state, 'azimuth_calculation'):
az_disabled = not st.session_state.azimuth_calculation
st.selectbox('Azimuth type', disabled=az_disabled, options=['Multiple', 'Single'], index=0, key='azimuth_type')
azAngCol, azUnitCol = st.columns([0.7,0.3])
azAngCol.number_input("Azimuth angle", value=30, key='azimuth_angle', disabled=az_disabled)
azUnitCol.selectbox('Azimuth unit', options=['°', 'rad'], index=0, key='azimuth_unit', disabled=az_disabled)
if VERBOSE:
print_param(PARAM2PRINT)
#@st.experimental_dialog("Update Parameters to Remove Noise and Outlier Curves", width='large')
#def open_outliernoise_dialog():
with rmnocSetTab:
if VERBOSE:
print('Setting up noise tab, session state length: ', len(st.session_state.keys()))
# Set up toggles and options
remNoiseCol, rawNoiseCol = st.columns([0.55, 0.45])
remNoiseCol.toggle("Remove Noise", value=False,
help='Whether to remove noise from input data.', key='noise_removal')
noiseRemDisabled = not st.session_state.noise_removal
rawNoiseCol.toggle("Raw data", disabled=noiseRemDisabled, help='Whether to use the raw input data to remove noise.', key='remove_raw_noise')
remNoisePopover = st.popover('Remove Noise options', disabled=noiseRemDisabled, width='stretch')
with remNoisePopover:
# Auto noise
st.toggle("Auto Noise Removal", value=False, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='auto_noise_removal')
st.divider()
do_stalta = False
do_sat = False
do_noise = False
do_stdev = False
do_warm = False
do_cool = False
do_outCurve = False
doNoiseRemList = [do_stalta, do_sat, do_noise, do_stdev, do_warm, do_cool]
autoRemList = [do_stdev, do_sat]
if not st.session_state.noise_removal:
for doNR in doNoiseRemList:
doNR = False
if st.session_state.auto_noise_removal and not any(autoRemList):
do_stdev = True
do_sat = True
# Standard devation ratio
st.toggle("StDev Ratio Noise Detection", value=do_stdev, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='stdev_noise_removal')
stDevDisabled = (not st.session_state.stdev_noise_removal) or noiseRemDisabled
#std_ratio_thresh=2.0, std_window_size=20.0, min_std_win=5.0,
st.number_input('Moving StDev. Threshold', min_value=0.0, max_value=100.0, step=0.1, value=2.0,
help='The threshold value of StDev_Moving / StDev_Total to use as a removal threshold',
format="%.1f", disabled=stDevDisabled, key='std_ratio_thresh')
mvStDWinCol, minStDWinCol = st.columns([0.5, 0.5])
mvStDWinCol.number_input('Moving StDev. Win Size (samples)', value=20,
help='The size of the window to use to calculate the rolling standard deviation',
disabled=stDevDisabled, step=1, key='std_window_size')
minStDWinCol.number_input('Min. Win. Size (samples)', value=5,
help="The minimum number of samples in a row that exceed the ratio threshold for that window to be removed",
disabled=stDevDisabled, step=1, key='min_std_win')
# Saturation threshold
st.toggle("Saturation Threshold Noise Detection", value=do_sat, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='sat_noise_removal')
satRemDisabled = (not st.session_state.sat_noise_removal) or noiseRemDisabled
st.number_input('Saturation Percent', value=0.99, min_value=0.0, max_value=1.0, step=0.01,
format="%.2f", disabled=satRemDisabled, key='sat_percent')
# STALTA
st.toggle("STALTA Noise Detection", value=do_stalta, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='stalta_noise_removal')
staltaDisabled = (not st.session_state.stalta_noise_removal) or noiseRemDisabled
staCol, ltaCol = st.columns([0.5,0.5])
staCol.number_input('Short Term Average (STA)', step=0.5, value=2.0,
help='Length of moving window (in seconds) for calculating STA average',
disabled=staltaDisabled, key='sta')
ltaCol.number_input('Long Term Average (LTA)', value=30.0,
help='Length of moving window (in seconds) for calculating LTA average',
disabled=staltaDisabled, step=0.5, key='lta')
staltaVals = np.arange(0, 51).tolist()
st.select_slider('STA/LTA Thresholds', disabled=staltaDisabled,
help='Trigger thresholds for STA/LTA calculation',
value=st.session_state.stalta_thresh, options=staltaVals, key='stalta_thresh')
# Noise threshold
st.toggle("Noise Threshold Noise Detection", value=do_noise, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='noise_thresh_noise_removal')
noiseDisabled = (not st.session_state.noise_thresh_noise_removal) or noiseRemDisabled
st.number_input('Noise Percent', value=0.8, min_value=0.0, max_value=1.0, step=0.05,
format="%.2f", disabled=noiseDisabled, key='noise_percent')
st.number_input('Minimum Window Size (samples)', value=1, step=1,
disabled=noiseDisabled, key='min_win_size')
# Warmup
st.toggle("Warmup Time Removal", value=do_warm, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='warmup_noise_removal')
warmupDisabled = (not st.session_state.warmup_noise_removal) or noiseRemDisabled
st.number_input('Warmup Time (seconds)', disabled=warmupDisabled, step=1, key='warmup_time')
# Cooldown
st.toggle("Cooldown Time Removal", value=do_cool, disabled=noiseRemDisabled,
help='Whether to remove noise from input data.', key='cooldown_noise_removal')
cooldownDisabled = (not st.session_state.cooldown_noise_removal) or noiseRemDisabled
st.number_input('Cooldown Time (seconds)', disabled=cooldownDisabled, step=1, key='cooldown_time')
def update_ROC():
ocr = st.session_state.outlier_curves_removal
if hasattr(st.session_state, 'outlier_method'):
if not ocr:
st.session_state.outlier_method=None
st.toggle("Remove Outlier Curves", value=False, on_change=update_ROC,
help='Whether to remove outlier curves from input data.', key='outlier_curves_removal')
outlierCurveDisabled = not st.session_state.outlier_curves_removal
# Outlier curves
remCurvePopover = st.popover('Remove Outlier Curve Options', disabled=outlierCurveDisabled, width='stretch')
with remCurvePopover:
st.selectbox("Outlier Detection Method", options=[None, 'Prototype', 'DBSCAN'], disabled=outlierCurveDisabled, index=0, key='outlier_method')
st.number_input("Outlier Threshold", disabled=outlierCurveDisabled, value=98, key='outlier_threshold')
st.radio('Threshold type', horizontal=True, disabled=outlierCurveDisabled, options=['Percentile', 'Value'], key='threshRadio')
st.session_state.use_percentile = st.session_state.threshRadio=='Percentile'
st.radio('Threshold curve', horizontal=True, disabled=outlierCurveDisabled, options=['HV Curve', 'Component Curves'], index=1, key='curveRadio')
st.session_state.use_hv_curves = (st.session_state.curveRadio=='HV Curve')
min_ptsDisabled = (st.session_state.outlier_method !='DBSCAN')
st.number_input("DBSCAN Minimum Neighborhood Size", disabled=min_ptsDisabled, value=5, key='min_pts')
#noise_rem_method_list = ['None', 'Auto', 'Manual', 'Stalta', 'Saturation Threshold', 'Noise Threshold', 'Warmup', 'Cooldown', 'Buffer']
#st.multiselect("Noise Removal Method",
# options=,
# key='remove_method')
if VERBOSE:
print_param(PARAM2PRINT)
#@st.experimental_dialog("Update Parameters to Generate PPSDs", width='large')
#def open_ppsd_dialog():
with gpSetTab:
if VERBOSE:
print('Setting up ppsd tab, session state length: ', len(st.session_state.keys()))
st.toggle('Skip on gaps', help='Determines whether time segments with gaps should be skipped entirely. Select skip_on_gaps=True for not filling gaps with zeros which might result in some data segments shorter than ppsd_length not used in the PPSD.',
key='skip_on_gaps')
st.number_input("Minimum Decibel Value", value=-200, step=1, key='min_deb')
st.number_input("Maximum Decibel Value", value=-50, step=1, key='max_deb')
st.number_input("Decibel bin size", value=1.0, step=0.1, key='deb_step')
st.session_state.db_bins = (st.session_state.min_deb, st.session_state.max_deb, st.session_state.deb_step)
st.number_input('PPSD Length (seconds)', step=1, key='ppsd_length')
st.number_input('PPSD Window overlap (%, 0-1)', step=0.01, min_value=0.0, max_value=1.0, key='overlap')
st.number_input('Period Smoothing Width (octaves)', step=0.1, key='period_smoothing_width_octaves')
st.number_input('Period Step (octaves)', step=0.005, format="%.5f", key='period_step_octaves')
periodVals=[round(1/x,3) for x in bandVals]
periodVals.sort()
st.select_slider('Period Limits (s)', options=periodVals, value=st.session_state.period_limits, key='period_limits')
st.selectbox("Special Handling", options=['None', 'Ringlaser', 'Hydrophone'], key='special_handling')
if VERBOSE:
print_param(PARAM2PRINT)
#@st.experimental_dialog("Update Parameters to Process HVSR", width='large')
#def open_processHVSR_dialog():
with phvsrSetTab:
if VERBOSE:
print('Setting up hvsr tab, session state length: ', len(st.session_state.keys()))
st.selectbox('Peak Selection Method', options=['Max', 'Scored'], key='peak_selection')
st.selectbox("Method to combine hoizontal components",
options=['Diffuse Field Assumption', 'Arithmetic Mean', 'Geometric Mean', 'Vector Summation', 'Quadratic Mean', 'Maximum Horizontal Value', 'Azimuth'],
index=2, key='horizontal_method')
rList = np.arange(1001).tolist()
rList[0] = False
st.selectbox("Curve Smoothing", options=['None', 'Savgoy Filter', 'Konno Ohmachi', "Proportional", "Constant"], index=2, key='freq_smooth')
st.select_slider("Curve Smoothing Parameter", options=np.arange(1000).tolist(), value=40, key='f_smooth_width')
st.select_slider("Resample", options=rList, value=1000, key='resample')
st.select_slider('Outlier Curve Removal', options=rList[:100], key='outlier_curve_percentile_threshold')
if VERBOSE:
print_param(PARAM2PRINT)
def update_plot_string():
plotStringDict={'Peak Frequency':' p', 'Peak Amplitude':' pa', 'Annotation':' ann',
'Time windows':' t', "Peaks of Time Windows": ' tp',
'Test 1: Peak > 2x trough below':'1',
"Test 2: Peak > 2x trough above":'2',
"Test 3: Peak > 2":'3',
"Test 4":'4', "Test 5":'5', "Test 6":'6',
}
plotString = ''
for plot in st.session_state.plotPlotStr:
if plot=='HVSR':
plotString=plotString+'HVSR'
for pc in st.session_state.hvsrPlotStr:
if 'test' in pc.lower():
if 'test' not in plotString.lower():
plotString = plotString + ' Test'
test_end_index = plotString.rfind("Test") + len("Test")
nextSpaceIndex = plotString[test_end_index:].rfind(" ")
if nextSpaceIndex == -1:
nextSpaceIndex=len(plotString)
noString = plotString[test_end_index:nextSpaceIndex]
noString = noString + plotStringDict[pc]
# Order test numbers correctly
testNos = ''.join(sorted(noString))
plotString = plotString[:test_end_index] + testNos
else:
plotString = plotString + plotStringDict[pc]
if plot=='Components':
plotString=plotString+' C+'
for pc in st.session_state.compPlotStr:
plotString = plotString + plotStringDict[pc]
if plot=='Spectrogram':
plotString=plotString+' SPEC'
for pc in st.session_state.specPlotStr:
plotString = plotString + plotStringDict[pc]
if plot=='Azimuth':
plotString=plotString+' AZ'
st.session_state.plot_type = plotString
#@st.experimental_dialog("Update Plot Settings", width='large')
#def plot_settings_dialog():
with plotSetTab:
if VERBOSE:
print('Setting up plot tab, session state length: ', len(st.session_state.keys()))
st.selectbox("Plot Engine", options=['Plotly', "Matplotlib"], key='plot_engine', disabled=False, help="Currently, interactive plots are only supported in plotly.")
st.text_input("Plot type (plot string)", value='HVSR p ann C+ p ann Spec p', key='plot_type')
st.multiselect("Charts to show", options=['HVSR', "Components", 'Spectrogram', 'Azimuth'], default=['HVSR', 'Components', "Spectrogram"],
on_change=update_plot_string, key='plotPlotStr')
st.header("HVSR Chart", divider='rainbow')
st.multiselect('Items to plot', options=['Peak Frequency', 'Peak Amplitude', 'Annotation', 'Time windows', "Peaks of Time Windows",
'Test 1: Peak > 2x trough below' , "Test 2: Peak > 2x trough above", "Test 3: Peak > 2", "Test 4", "Test 5", "Test 6"],
on_change=update_plot_string,
default=["Peak Frequency", "Annotation"], key='hvsrPlotStr')
st.header("Component Chart", divider='rainbow')
st.multiselect('Items to plot', options=['Peak Frequency', 'Annotation', 'Time windows'], on_change=update_plot_string,
default=["Peak Frequency", "Annotation"], key='compPlotStr')
st.header('Spectrogram Chart', divider='rainbow')
st.multiselect('Items to plot', options=['Peak Frequency', 'Annotation'], key='specPlotStr', on_change=update_plot_string,
default=["Peak Frequency", "Annotation"])
if VERBOSE:
print_param(PARAM2PRINT)
st.button('View Logs',key='view_logs', help='Click to view logs on the main screen, or select the Logs tab if available',
on_click=show_logs)
if VERBOSE:
print('Done setting up sidebar, session state length: ', len(st.session_state.keys()))
print('Done setting up everything (end of main), session state length: ', len(st.session_state.keys()))
print_param(PARAM2PRINT)
if __name__ == "__main__":
main()