Gitdeeper4's picture
ุชุญุฏูŠุซ ูƒุงู…ู„ ู…ุน ู†ูุณ ุชุตู…ูŠู… ุงู„ุตูุญุฉ ุงู„ุฑุณู…ูŠุฉ
a47e2dd
"""
TSU-WAVE Dashboard - Streamlit Interface
Main dashboard for visualizing tsunami parameters and CHI index
Complete redesign matching official TSUNAMI landing page style
"""
import streamlit as st
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime, timedelta
import time
import sys
import os
import random
# Add main path to PYTHONPATH
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
# Try to import TSU-WAVE modules (optional)
try:
from tsuwave import TSUWave
from tsuwave.core.chi import compute_chi
from tsuwave.core.parameters import (
compute_wcc, compute_kpr, compute_hfsi,
compute_becf, compute_sdb, compute_sbsp, compute_smvi
)
TSUWAVE_AVAILABLE = True
except ImportError:
TSUWAVE_AVAILABLE = False
# Page configuration
st.set_page_config(
page_title="TSUNAMI - Early Warning System",
page_icon="๐ŸŒŠ",
layout="wide",
initial_sidebar_state="expanded"
)
# ========== OFFICIAL CSS FROM INDEX.HTML ==========
st.markdown("""
<style>
/* Global Styles - Direct from official page */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;font-size:18px}
body{font-family:'Segoe UI',Arial,sans-serif;background:#020b18;color:#e0f4ff;overflow-x:hidden;line-height:1.6}
/* Main header */
.main-header {
background: radial-gradient(ellipse at 50% 85%,rgba(0,119,182,.45) 0%,transparent 65%),linear-gradient(180deg,#020b18 0%,#03045e 55%,#0077b6 100%);
padding: 4rem 2rem;
border-radius: 0 0 30px 30px;
margin-bottom: 2rem;
position: relative;
overflow: hidden;
text-align: center;
}
/* Wave animations */
.waves{position:relative;height:100px;overflow:hidden;margin-top:-50px}
.waves svg{position:absolute;bottom:0;width:200%;animation:wm 10s linear infinite}
.waves svg:nth-child(2){animation-duration:15s;animation-direction:reverse;opacity:.6;bottom:8px}
.waves svg:nth-child(3){animation-duration:20s;opacity:.4;bottom:16px}
@keyframes wm{from{transform:translateX(0)}to{transform:translateX(-50%)}}
/* Badge */
.badge{display:inline-block;margin-bottom:1.8rem;border:1px solid #f72585;color:#f72585;background:rgba(247,37,133,.12);padding:.5rem 1.4rem;border-radius:50px;font-size:1rem;letter-spacing:2px;text-transform:uppercase;font-weight:600}
/* Title */
.ht{font-size:clamp(3.4rem,9vw,6.5rem);font-weight:900;line-height:1.05;color:#fff;text-shadow:0 0 60px rgba(0,180,216,.55);margin-bottom:.6rem}
.ht span{color:#00b4d8}
.hl{font-size:clamp(1.05rem,2.2vw,1.25rem);color:#90e0ef;letter-spacing:.5px;margin-bottom:1.1rem;font-style:italic}
.hd{font-size:clamp(1.05rem,2vw,1.2rem);color:#caf0f8;line-height:1.8;margin-bottom:2.8rem}
/* Buttons */
.btn{width:100%;max-width:360px;padding:1.05rem 1.8rem;border-radius:10px;font-size:1.15rem;font-weight:700;text-decoration:none;border:none;cursor:pointer;transition:all .25s;text-align:center;display:inline-block;margin:0.5rem}
.bp{background:linear-gradient(135deg,#0077b6,#00b4d8);color:#fff;box-shadow:0 4px 20px rgba(0,180,216,.4)}
.bp:hover{transform:translateY(-2px);box-shadow:0 8px 28px rgba(0,180,216,.5)}
.bd{background:linear-gradient(135deg,#e63946,#f72585);color:#fff;box-shadow:0 4px 20px rgba(247,37,133,.35)}
.bd:hover{transform:translateY(-2px)}
.bo{background:transparent;color:#00b4d8;border:2px solid #00b4d8}
.bo:hover{background:rgba(0,180,216,.1);transform:translateY(-2px)}
/* Stats cards */
.stats{padding:3.5rem 1.5rem;background:rgba(0,15,40,.65);border-radius:20px;margin:2rem 0}
.stats-title{text-align:center;font-size:1.05rem;font-weight:700;letter-spacing:3px;text-transform:uppercase;color:#00b4d8;margin-bottom:2rem}
.sc{display:flex;flex-direction:column;gap:1.1rem;max-width:620px;margin:0 auto}
.sc-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,180,216,.2);border-radius:16px;padding:1.6rem 2.2rem;display:flex;align-items:center;gap:2rem;backdrop-filter:blur(10px);transition:all .3s;position:relative;overflow:hidden;width:100%}
.sc-card::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(0,180,216,.07) 0%,transparent 60%);pointer-events:none}
.sc-card:hover{border-color:rgba(0,180,216,.5);transform:translateX(5px);background:rgba(0,180,216,.08)}
.sc-card .sn{font-size:clamp(2.4rem,5vw,3.2rem);font-weight:900;color:#00b4d8;line-height:1;letter-spacing:-1px;min-width:140px}
.sc-card .sl{font-size:1.15rem;color:#caf0f8;line-height:1.4;font-weight:500}
/* Section headers */
section{padding:2rem 1.5rem}
.stl{font-size:clamp(2rem,4vw,2.8rem);font-weight:800;color:#fff;text-align:center;margin-bottom:.6rem}
.ssb{text-align:center;color:#90e0ef;font-size:1.15rem;margin-bottom:.6rem;line-height:1.6}
.div{width:60px;height:4px;background:linear-gradient(90deg,#0077b6,#00b4d8);border-radius:2px;margin:1rem auto 3rem}
/* Feature cards */
.fl{display:flex;flex-direction:column;gap:2.6rem}
.fi{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.9rem;background:rgba(3,4,94,.22);padding:2rem;border-radius:20px}
.fi:hover{background:rgba(0,119,182,.15);transform:translateY(-5px);transition:all .3s}
.fic{font-size:3rem}
.fi h3{font-size:1.4rem;font-weight:700;color:#fff}
.fi p{font-size:1.1rem;color:#90e0ef;line-height:1.75;max-width:600px}
/* Parameter cards */
.pl{display:flex;flex-direction:column;gap:1.1rem}
.pi{background:rgba(0,119,182,.07);border-left:4px solid #00b4d8;border-radius:0 14px 14px 0;padding:1.4rem 1.8rem}
.pi:hover{background:rgba(0,119,182,.15);transform:translateX(5px);transition:all .3s}
.pi h4{font-size:1.15rem;font-weight:700;color:#fff;margin-bottom:.4rem}
.pi p{font-size:1.05rem;color:#90e0ef;line-height:1.65}
.pt{display:inline-block;margin-top:.6rem;font-size:1rem;color:#00b4d8;background:rgba(0,180,216,.1);border:1px solid rgba(0,180,216,.35);padding:.25rem .8rem;border-radius:5px;font-weight:600}
/* Alert levels - exactly as in official page */
.al{display:flex;flex-direction:column;gap:1.5rem}
.ac{border-radius:18px;padding:2.2rem 2rem;border:1px solid transparent;display:flex;flex-direction:column;align-items:center;text-align:center;gap:.8rem;backdrop-filter:blur(10px);position:relative;overflow:hidden;transition:all .3s}
.ac::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.06) 0%,transparent 60%);pointer-events:none}
.ac:hover{transform:translateY(-4px)}
.l1{background:rgba(0,200,100,.07);border-color:rgba(0,200,100,.3)}
.l1:hover{background:rgba(0,200,100,.12);border-color:rgba(0,200,100,.6)}
.l2{background:rgba(255,200,0,.07);border-color:rgba(255,200,0,.3)}
.l2:hover{background:rgba(255,200,0,.12);border-color:rgba(255,200,0,.6)}
.l3{background:rgba(255,130,0,.07);border-color:rgba(255,130,0,.3)}
.l3:hover{background:rgba(255,130,0,.12);border-color:rgba(255,130,0,.6)}
.l4{background:rgba(230,40,50,.08);border-color:rgba(230,40,50,.35)}
.l4:hover{background:rgba(230,40,50,.14);border-color:rgba(230,40,50,.65)}
.alv{font-size:1rem;font-weight:800;letter-spacing:3px;text-transform:uppercase;padding:.45rem 1.2rem;border-radius:50px}
.l1 .alv{color:#4dffb0;background:rgba(77,255,176,.12);border:1px solid rgba(77,255,176,.3)}
.l2 .alv{color:#ffe566;background:rgba(255,229,102,.12);border:1px solid rgba(255,229,102,.3)}
.l3 .alv{color:#ffac45;background:rgba(255,172,69,.12);border:1px solid rgba(255,172,69,.3)}
.l4 .alv{color:#ff4d5e;background:rgba(255,77,94,.12);border:1px solid rgba(255,77,94,.3)}
.ac-level-num{font-size:clamp(2.8rem,7vw,4rem);font-weight:900;line-height:1;margin:.2rem 0}
.l1 .ac-level-num{color:#4dffb0}
.l2 .ac-level-num{color:#ffe566}
.l3 .ac-level-num{color:#ffac45}
.l4 .ac-level-num{color:#ff4d5e}
.ac h4{font-size:1.4rem;font-weight:700;color:#fff}
.ac p{font-size:1.1rem;color:#caf0f8;line-height:1.7;max-width:520px}
/* Metric cards for parameters */
.metric-card {
background: rgba(0,119,182,.07);
border-left: 4px solid #00b4d8;
border-radius: 0 14px 14px 0;
padding: 1rem 1.5rem;
margin-bottom: 0.5rem;
}
.metric-card:hover {
background: rgba(0,119,182,.15);
transform: translateX(5px);
transition: all .3s;
}
.metric-value {
font-size: 1.8rem;
font-weight: 700;
color: #00b4d8;
}
.metric-label {
font-size: 0.9rem;
color: #90e0ef;
}
/* Footer */
footer{background:#010d1a;border-top:1px solid rgba(0,180,216,.2);padding:3.5rem 1.5rem 2rem;color:#90e0ef;margin-top:3rem}
.fb{max-width:1200px;margin:0 auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:2.8rem}
.fbn{font-size:1.5rem;font-weight:800;color:#00b4d8;margin-bottom:.5rem}
.fbd{font-size:1.05rem;color:#90e0ef;line-height:1.65}
.fbv{font-size:.95rem;color:rgba(144,224,239,.5);margin-top:.5rem}
.fc h4{font-size:1.15rem;font-weight:700;color:#fff;margin-bottom:1.1rem}
.fc ul{list-style:none;padding:0}
.fc ul li{margin-bottom:.75rem}
.fc ul li a{color:#90e0ef;text-decoration:none;font-size:1.05rem;transition:color .25s}
.fc ul li a:hover{color:#00b4d8}
.fbot{text-align:center;margin-top:2rem;padding-top:1.5rem;border-top:1px solid rgba(0,180,216,.12);font-size:1rem;color:rgba(144,224,239,.5)}
/* Responsive */
@media(max-width:768px){
.sc-card{flex-direction:column;align-items:flex-start;gap:.5rem;padding:1.4rem 1.6rem}
.sc-card .sn{min-width:unset}
}
</style>
""", unsafe_allow_html=True)
# ========== JAVASCRIPT FROM OFFICIAL PAGE ==========
st.markdown("""
<script>
// Smooth scroll for navigation
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// Mobile menu toggle (for any future mobile implementation)
function toggleMenu() {
const navLinks = document.querySelector('.nav-links');
if (navLinks) navLinks.classList.toggle('open');
}
// Performance monitoring
const performanceData = {
loadTime: performance.now(),
interactions: 0
};
// Track user interactions
document.addEventListener('click', function() {
performanceData.interactions++;
console.log('Dashboard ready - User interactions:', performanceData.interactions);
});
// Initialize on page load
window.addEventListener('load', function() {
console.log('TSUNAMI Dashboard loaded in', (performance.now() - performanceData.loadTime).toFixed(2), 'ms');
// Add wave animation class to main header
const header = document.querySelector('.main-header');
if (header) {
header.classList.add('waves-animated');
}
});
// Handle visibility change (pause animations when tab not active)
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
console.log('Dashboard paused');
} else {
console.log('Dashboard resumed');
}
});
</script>
""", unsafe_allow_html=True)
# Initialize session state
if 'auto_refresh' not in st.session_state:
st.session_state.auto_refresh = False
if 'last_update' not in st.session_state:
st.session_state.last_update = datetime.now()
if 'selected_zone' not in st.session_state:
st.session_state.selected_zone = "hilo_bay_hawaii"
if 'chi_history' not in st.session_state:
st.session_state.chi_history = []
# ========== MAIN HEADER WITH WAVES (EXACTLY LIKE INDEX.HTML) ==========
st.markdown("""
<div class="main-header">
<div class="badge">โš ๏ธ Ocean Disaster Early Warning System</div>
<h1 class="ht">๐ŸŒŠ <span>TSUNAMI</span></h1>
<p class="hl">Tidal-wave Unified System for Undersea Notification and Alert Management Integration</p>
<p class="hd">An intelligent real-time framework for early detection of tsunami waves and coastal community protection โ€” powered by AI, seismic sensors, and global satellite networks.</p>
<div>
<a href="#dashboard" class="btn bp">๐Ÿ” Launch Dashboard</a>
<a href="#alerts-section" class="btn bd">๐Ÿšจ Alert Levels</a>
<a href="#footer" class="btn bo">๐Ÿ“ก Contact</a>
</div>
</div>
<div class="waves">
<svg viewBox="0 0 1440 100" preserveAspectRatio="none" style="height:100px"><path fill="rgba(0,119,182,.45)" d="M0,50 C360,100 1080,0 1440,50 L1440,100 L0,100Z"/><path fill="rgba(0,119,182,.45)" d="M1440,50 C1080,100 360,0 0,50 L0,100 L1440,100Z"/></svg>
<svg viewBox="0 0 1440 100" preserveAspectRatio="none" style="height:100px"><path fill="rgba(0,180,216,.3)" d="M0,35 C480,90 960,0 1440,35 L1440,100 L0,100Z"/><path fill="rgba(0,180,216,.3)" d="M1440,35 C960,90 480,0 0,35 L0,100 L1440,100Z"/></svg>
<svg viewBox="0 0 1440 100" preserveAspectRatio="none" style="height:100px"><path fill="rgba(144,224,239,.18)" d="M0,70 C300,20 1100,90 1440,45 L1440,100 L0,100Z"/><path fill="rgba(144,224,239,.18)" d="M1440,70 C1100,20 300,90 0,45 L0,100 L1440,100Z"/></svg>
</div>
""", unsafe_allow_html=True)
# ========== STATS CARDS (EXACTLY LIKE INDEX.HTML) ==========
st.markdown("""
<div class="stats">
<p class="stats-title">โ€” Live System Performance โ€”</p>
<div class="sc">
<div class="sc-card"><div class="sn">98.6%</div><div class="sl">Early Detection Accuracy</div></div>
<div class="sc-card"><div class="sn">< 3 min</div><div class="sl">Alert Response Time</div></div>
<div class="sc-card"><div class="sn">120+</div><div class="sl">Ocean Monitoring Stations</div></div>
<div class="sc-card"><div class="sn">47</div><div class="sl">Countries Covered</div></div>
<div class="sc-card"><div class="sn">24/7</div><div class="sl">Continuous Monitoring</div></div>
</div>
</div>
""", unsafe_allow_html=True)
# Mock data function
def get_mock_data(zone):
"""Generate realistic mock data for display"""
np.random.seed(hash(zone) % 100 + int(time.time()) % 1000)
# Base values with some randomness
base_chi = 0.35 + np.random.random() * 0.5
return {
'chi': base_chi,
'wcc': 1.2 + np.random.random() * 0.8,
'kpr': 1.5 + np.random.random() * 1.2,
'hfsi': 0.3 + np.random.random() * 0.3,
'becf': 4.0 + np.random.random() * 4.0,
'sdb': 0.8 + np.random.random() * 0.6,
'sbsp': 0.9 + np.random.random() * 0.8,
'smvi': 0.4 + np.random.random() * 0.5
}
# Get data
if TSUWAVE_AVAILABLE:
try:
tsw = TSUWave()
chi = tsw.get_chi(zone=st.session_state.selected_zone)
params = tsw.get_parameters(zone=st.session_state.selected_zone)
except Exception as e:
st.warning(f"โš ๏ธ TSU-WAVE error: {e}. Using simulation data.")
data = get_mock_data(st.session_state.selected_zone)
chi = data['chi']
params = {k: v for k, v in data.items() if k != 'chi'}
else:
data = get_mock_data(st.session_state.selected_zone)
chi = data['chi']
params = {k: v for k, v in data.items() if k != 'chi'}
# Update history
st.session_state.chi_history.append({
'time': datetime.now(),
'chi': chi,
'zone': st.session_state.selected_zone
})
if len(st.session_state.chi_history) > 100:
st.session_state.chi_history = st.session_state.chi_history[-100:]
# ========== DASHBOARD SECTION ==========
st.markdown('<div id="dashboard"></div>', unsafe_allow_html=True)
st.markdown('<h2 class="stl">๐ŸŒŠ Live Tsunami Dashboard</h2>', unsafe_allow_html=True)
st.markdown('<p class="ssb">Real-time monitoring of seven hydrodynamic parameters</p>', unsafe_allow_html=True)
st.markdown('<div class="div"></div>', unsafe_allow_html=True)
# Sidebar for controls
with st.sidebar:
st.markdown('<div style="background:#020b18; padding:1rem; border-radius:10px">', unsafe_allow_html=True)
st.markdown('<h3 style="color:#00b4d8">โš™๏ธ Dashboard Controls</h3>', unsafe_allow_html=True)
# Zone selection
zones = {
"hilo_bay_hawaii": "๐ŸŒบ Hilo Bay, Hawaii",
"khao_lak": "๐Ÿ๏ธ Khao Lak, Thailand",
"sendai": "๐Ÿ—พ Sendai, Japan",
"illapel": "๐Ÿ”๏ธ Illapel, Chile",
"peru": "๐Ÿœ๏ธ Peru Coast"
}
selected_zone = st.selectbox(
"Select Coastal Zone",
options=list(zones.keys()),
format_func=lambda x: zones[x],
index=0
)
st.session_state.selected_zone = selected_zone
# Auto-refresh
auto_refresh = st.checkbox(
"๐Ÿ”„ Auto-refresh (every 30s)",
value=st.session_state.auto_refresh
)
if auto_refresh != st.session_state.auto_refresh:
st.session_state.auto_refresh = auto_refresh
st.rerun()
refresh_interval = 30
if st.session_state.auto_refresh:
refresh_interval = st.slider(
"Refresh interval (seconds)",
min_value=10,
max_value=120,
value=30,
step=10
)
# Manual refresh
if st.button("๐Ÿ”„ Refresh Now", use_container_width=True):
st.session_state.last_update = datetime.now()
st.rerun()
# System info
st.markdown("---")
st.markdown(f"**Last Update:** {st.session_state.last_update.strftime('%H:%M:%S')}")
st.markdown(f"**Status:** {'๐ŸŸข Live' if TSUWAVE_AVAILABLE else '๐ŸŸก Simulation'}")
st.markdown("</div>", unsafe_allow_html=True)
# Main metrics
col1, col2 = st.columns([1, 1])
with col1:
st.markdown(f"""
<div class="metric-card">
<div class="metric-value">{chi:.3f}</div>
<div class="metric-label">Composite Hazard Index (CHI)</div>
<span class="pt">Threshold: {'๐ŸŸข' if chi < 0.35 else '๐ŸŸก' if chi < 0.55 else '๐ŸŸ ' if chi < 0.75 else '๐Ÿ”ด'} {chi:.3f}</span>
</div>
""", unsafe_allow_html=True)
# Alert level
if chi < 0.35:
alert_class = "l1"
alert_level = "LEVEL 01"
alert_title = "Monitor"
alert_desc = "No significant hazard. Passive monitoring."
elif chi < 0.55:
alert_class = "l2"
alert_level = "LEVEL 02"
alert_title = "Watch"
alert_desc = "Elevated โ€” Advisory issued. Heightened readiness."
elif chi < 0.75:
alert_class = "l3"
alert_level = "LEVEL 03"
alert_title = "Warning"
alert_desc = "High โ€” Evacuation recommended. Activate protocols."
else:
alert_class = "l4"
alert_level = "LEVEL 04"
alert_title = "Extreme"
alert_desc = "Imminent โ€” Immediate evacuation. Full emergency response."
st.markdown(f"""
<div class="ac {alert_class}" style="margin-top:1rem">
<div class="alv">{alert_level}</div>
<div class="ac-level-num">{chi:.3f}</div>
<h4>{alert_title}</h4>
<p>{alert_desc}</p>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown('<h4 style="color:#fff">Seven Hydrodynamic Parameters</h4>', unsafe_allow_html=True)
st.markdown(f"""
<div class="metric-card"><span class="metric-value">{params.get('wcc', 0):.3f}</span> <span class="metric-label">WCC - Wave Front Celerity</span> <span class="pt">>1.58</span></div>
<div class="metric-card"><span class="metric-value">{params.get('kpr', 0):.3f}</span> <span class="metric-label">KPR - Kinetic/Potential Ratio</span> <span class="pt">>2.0</span></div>
<div class="metric-card"><span class="metric-value">{params.get('hfsi', 0):.3f}</span> <span class="metric-label">HFSI - Front Stability</span> <span class="pt"><0.40</span></div>
<div class="metric-card"><span class="metric-value">{params.get('becf', 0):.3f}</span> <span class="metric-label">BECF - Bathymetric Concentration</span> <span class="pt">>6.0</span></div>
<div class="metric-card"><span class="metric-value">{params.get('sdb', 0):.3f}</span> <span class="metric-label">SDB - Spectral Dispersion</span> <span class="pt"><1.0</span></div>
<div class="metric-card"><span class="metric-value">{params.get('sbsp', 0):.3f}</span> <span class="metric-label">SBSP - Shoreline Stress</span> <span class="pt">>1.2</span></div>
<div class="metric-card"><span class="metric-value">{params.get('smvi', 0):.3f}</span> <span class="metric-label">SMVI - Micro-Vorticity</span> <span class="pt">>0.6</span></div>
""", unsafe_allow_html=True)
# CHI History Chart
st.markdown('<div class="chart-container" style="background:rgba(0,15,40,.65); border-radius:20px; padding:2rem; margin:2rem 0">', unsafe_allow_html=True)
st.markdown('<h4 style="color:#fff">๐Ÿ“ˆ CHI Evolution Over Time</h4>', unsafe_allow_html=True)
if len(st.session_state.chi_history) > 1:
df = pd.DataFrame(st.session_state.chi_history)
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df['time'],
y=df['chi'],
mode='lines+markers',
name='CHI',
line=dict(color='#00b4d8', width=3),
marker=dict(size=8, color='#0077b6')
))
# Add threshold zones
fig.add_hrect(y0=0.35, y1=0.55, line_width=0, fillcolor="#ffe566", opacity=0.1, annotation_text="WATCH")
fig.add_hrect(y0=0.55, y1=0.75, line_width=0, fillcolor="#ffac45", opacity=0.1, annotation_text="WARNING")
fig.add_hrect(y0=0.75, y1=1.0, line_width=0, fillcolor="#ff4d5e", opacity=0.1, annotation_text="EXTREME")
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(color='#90e0ef'),
xaxis=dict(gridcolor='rgba(144,224,239,.1)'),
yaxis=dict(gridcolor='rgba(144,224,239,.1)'),
hovermode='x unified'
)
st.plotly_chart(fig, use_container_width=True)
st.markdown('</div>', unsafe_allow_html=True)
# ========== ALERT LEVELS SECTION (EXACTLY LIKE INDEX.HTML) ==========
st.markdown('<div id="alerts-section"></div>', unsafe_allow_html=True)
st.markdown('<h2 class="stl">๐Ÿšจ Alert Levels</h2>', unsafe_allow_html=True)
st.markdown('<p class="ssb">A graduated warning system ensuring the right response at every stage</p>', unsafe_allow_html=True)
st.markdown('<div class="div"></div>', unsafe_allow_html=True)
st.markdown("""
<div class="al">
<div class="ac l1">
<div class="alv">LEVEL 01</div>
<div class="ac-level-num">01</div>
<h4>Watch</h4>
<p>Weak seismic activity detected and under surveillance. No immediate danger. Monitor official channels and stay informed.</p>
</div>
<div class="ac l2">
<div class="alv">LEVEL 02</div>
<div class="ac-level-num">02</div>
<h4>Advisory</h4>
<p>Low but non-zero probability of wave formation. Stay away from beaches and harbors. Await further instructions from authorities.</p>
</div>
<div class="ac l3">
<div class="alv">LEVEL 03</div>
<div class="ac-level-num">03</div>
<h4>Warning</h4>
<p>Real and confirmed threat. Move immediately to higher ground. Do not return to the coast until the all-clear is issued.</p>
</div>
<div class="ac l4">
<div class="alv">LEVEL 04</div>
<div class="ac-level-num">04</div>
<h4>Imminent Danger</h4>
<p>Wave confirmed and approaching with high confidence. Evacuate the coastal zone immediately. Follow designated evacuation routes. Do not wait.</p>
</div>
</div>
""", unsafe_allow_html=True)
# ========== CTA SECTION ==========
st.markdown("""
<div style="background:linear-gradient(160deg,#03045e 0%,#0077b6 100%); padding:4rem 2rem; border-radius:20px; text-align:center; margin:3rem 0">
<h2 style="font-size:clamp(2rem,4vw,2.8rem); font-weight:900; color:#fff; margin-bottom:1rem">Is Your Coastal Community Protected?</h2>
<p style="color:#caf0f8; margin-bottom:2.8rem; font-size:1.2rem">Join the global TSUNAMI network and ensure your region is ready before the wave begins.</p>
<div>
<a href="#" class="btn bp">๐Ÿ“ก Request Coverage</a>
<a href="#" class="btn bo">๐Ÿ“„ Technical Documentation</a>
</div>
</div>
""", unsafe_allow_html=True)
# Auto-refresh logic
if st.session_state.auto_refresh:
st.caption(f"๐Ÿ”„ Auto-refreshing every {refresh_interval} seconds...")
time.sleep(refresh_interval)
st.rerun()
# ========== FOOTER (EXACTLY LIKE INDEX.HTML) ==========
st.markdown("""
<footer id="footer">
<div class="fb">
<div>
<div class="fbn">๐ŸŒŠ TSUNAMI</div>
<p class="fbd">Integrated Early Warning System for Tsunami Waves and Coastal Community Protection</p>
<p class="fbv">Version 1.0.0 (AI Edition) โ€” 2026</p>
</div>
<div class="fc">
<h4>Quick Links</h4>
<ul>
<li><a href="#home">Home Page</a></li>
<li><a href="#features">Key Features</a></li>
<li><a href="#how">How It Works</a></li>
<li><a href="#params">Parameters</a></li>
<li><a href="#alerts-section">Alert Levels</a></li>
</ul>
</div>
<div class="fc">
<h4>Resources</h4>
<ul>
<li><a href="https://gitlab.com/gitdeeper4/tsu-wave/-/wikis/home" target="_blank">Wikis Documentation</a></li>
<li><a href="https://doi.org/10.5281/zenodo.18667713" target="_blank">Research Paper (DOI)</a></li>
<li><a href="https://pypi.org/project/stalwart-bridge/" target="_blank">PyPI Package</a></li>
<li><a href="https://osf.io/anrtv" target="_blank">OSF Repository</a></li>
<li><a href="https://huggingface.co/spaces/Gitdeeper4/tsunami" target="_blank">Hugging Face</a></li>
</ul>
</div>
<div class="fc">
<h4>Platforms</h4>
<ul>
<li><a href="https://gitlab.com/gitdeeper4/tsu-wave" target="_blank">GitLab</a></li>
<li><a href="https://github.com/gitdeeper4/tsu-wave" target="_blank">GitHub</a></li>
<li><a href="https://codeberg.org/gitdeeper4/tsu-wave" target="_blank">Codeberg</a></li>
<li><a href="https://bitbucket.org/gitdeeper7/tsu-wave" target="_blank">Bitbucket</a></li>
</ul>
</div>
</div>
<div class="fbot">Copyright &copy; TSUNAMI ๐ŸŒŠ โ€” 2026 | All rights reserved</div>
</footer>
""", unsafe_allow_html=True)
def run_dashboard():
"""Entry point for running the dashboard"""
pass
if __name__ == "__main__":
run_dashboard()