diff --git a/README.md b/README.md index a91b61d..3aee200 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ The original mapping used in the repository is: ## 🧠 Model Performance -The ML pipeline was trained on the **Earthquake Alert Prediction Dataset** using `RandomizedSearchCV` for hyperparameter tuning and **SMOTE** to handle class imbalance. +The ML pipeline was trained on the **Earthquake Alert Prediction Dataset** using `XGBClassifier` for hyperparameter tuning and **SMOTE** to handle class imbalance. The model configuration was chosen by running `RandomizedSearchCV`. **Classification Report on Test Set (260 samples):** @@ -192,8 +192,6 @@ SEISMOSENSE/ - Add user authentication and log predictions for research purposes. -**Note:** Due to the absence of front-end web development expertise and the absence of other contributors for the project, I was compelled to utilize AI tools (such as LLM services like ChatGPT™, Grok™, and GitHub Copilot™) to develop a sophisticated front-end for the web app. I am looking forward to human contribution on this project in order to scale it even further. - --- ## 📄 License diff --git a/app.py b/app.py index 9e0f47c..a192d6f 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,13 @@ # app.py -from flask import Flask, render_template, request +from flask import Flask, render_template, request, redirect, url_for, session import joblib import numpy as np from pathlib import Path from fit import main +import secrets app = Flask(__name__) +app.secret_key = secrets.token_hex(32) # paths to the pickle files MODEL_PATH = Path("models/estimator.pkl") @@ -36,8 +38,6 @@ def ensure_models(): # main routes @app.route("/", methods=["GET", "POST"]) def index(): - result = None - confidence = None if request.method == "POST": try: magnitude = float(request.form["magnitude"]) @@ -49,14 +49,23 @@ def index(): X = np.array([[magnitude, depth, cdi, mmi, sig]]) pred_label = model.predict(X)[0] + confidence = None if hasattr(model.named_steps["model"], "predict_proba"): proba_array = model.predict_proba(X)[0] - confidence = round(100 * proba_array[pred_label], 2) + confidence = round((float(100 * proba_array[pred_label])),2) - result = label_map[pred_label] + session["result"] = label_map[pred_label] + session["confidence"] = confidence except Exception as e: - result = f"Error: {str(e)}" + session["result"] = f"Error: {str(e)}" + session["confidence"] = None + # PRG: redirect so a page refresh won't re-submit the form + return redirect(url_for("index")) + + # GET: consume the result from session (one-time display) + result = session.pop("result", None) + confidence = session.pop("confidence", None) return render_template("index.html", result=result, confidence=confidence) if __name__ == "__main__": diff --git a/conf_mat.py b/conf_mat.py index 1b94db7..9f12e74 100644 --- a/conf_mat.py +++ b/conf_mat.py @@ -34,7 +34,7 @@ def load_data(path="dataset/earthquake_data.csv") -> np.ndarray: def plotting(x, y) -> None: x_train, x_test, y_train, y_test = train_test_split( - x, y, test_size=2/10, random_state=120, shuffle=True, stratify=y + x, y, test_size=2/10, random_state=4, shuffle=True, stratify=y ) labels = ALERT_LABELS @@ -46,11 +46,10 @@ def plotting(x, y) -> None: # Professional dark theme matching the frontend plt.style.use('dark_background') - BG_COLOR = '#0f1419' # Dark background - CARD_BG = '#1a1f26' # Card background + BG_COLOR = '#151C26' # Dark background + CARD_BG = "#050506" # Card background TEXT_PRIMARY = '#e2e8f0' # Primary text TEXT_SECONDARY = '#94a3b8' # Muted text - ACCENT = '#00d4aa' # Teal accent # Alert-specific colors for the heatmap ALERT_HEATMAP_COLORS = [ diff --git a/static/confusion_matrix.png b/static/confusion_matrix.png new file mode 100644 index 0000000..d5d263b Binary files /dev/null and b/static/confusion_matrix.png differ diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..8fd3f37 --- /dev/null +++ b/static/script.js @@ -0,0 +1,119 @@ +// ======================================== +// SeismoSense - JavaScript +// ======================================== + +// Tab Navigation +const tabs = document.querySelectorAll('.nav-tab'); +const panels = document.querySelectorAll('.tab-panel'); + +tabs.forEach(tab => { + tab.addEventListener('click', () => { + // Remove active class from all tabs and panels + tabs.forEach(t => t.classList.remove('active')); + panels.forEach(p => p.classList.remove('active')); + + // Add active class to clicked tab and corresponding panel + tab.classList.add('active'); + const panelId = tab.dataset.tab + '-panel'; + document.getElementById(panelId).classList.add('active'); + }); +}); + +// Toast Notification Functions +function getToastIcon(type) { + const icons = { + success: '', + error: '', + warning: '', + info: '' + }; + return icons[type] || icons.info; +} + +function showToast(message, type = 'info') { + const container = document.getElementById('toast-container'); + + if (!container) return; + + const toast = document.createElement('div'); + toast.className = `toast ${type}`; + toast.innerHTML = ` + + ${getToastIcon(type)} + + ${message} + `; + container.appendChild(toast); + setTimeout(() => { + toast.classList.add('toast-out'); + setTimeout(() => { + toast.remove(); + }, 300); + }, 3000); +} + +// Form Submission - Show loading state and toast +const form = document.getElementById('seismoForm'); +const btn = document.getElementById('predictBtn'); +const btnText = btn ? btn.querySelector('.btn-text') : null; +const loader = document.getElementById('loader'); + +if (form && btn && btnText) { + form.addEventListener('submit', () => { + // Show loading state + btn.disabled = true; + btnText.innerHTML = ' Analyzing...'; + + if (loader) { + loader.style.display = 'inline-block'; + } + }); +} + +// Clear button — reset all inputs +const clearBtn = document.getElementById('clearBtn'); +if (clearBtn && form) { + clearBtn.addEventListener('click', () => { + form.querySelectorAll('.form-input').forEach(input => { + input.value = ''; + }); + form.querySelector('.form-input').focus(); + }); +} + + +// On page load, reset button state and animate confidence bar +document.addEventListener('DOMContentLoaded', () => { + // Reset button state (in case of page reload with result) + if (btn && btnText) { + btn.disabled = false; + btnText.innerHTML = ' Analyze & Predict'; + + if (loader) { + loader.style.display = 'none'; + } + } + + // Show toast if there's a result + const resultCard = document.querySelector('.result-card'); + if (resultCard) { + const result = resultCard.classList.contains('green') ? 'Green Alert' : + resultCard.classList.contains('orange') ? 'Orange Alert' : + resultCard.classList.contains('red') ? 'Red Alert' : + resultCard.classList.contains('yellow') ? 'Yellow Alert' : null; + if (result) { + showToast('Prediction complete!', 'success'); + } + } + + // Animate confidence bar if result exists + const confFill = document.getElementById('confFill'); + const confValue = document.getElementById('confValue'); + + if (confFill && confValue) { + const width = confValue.textContent.trim(); + setTimeout(() => { + confFill.style.width = width + '%'; + }, 100); + } +}); diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..4cbe0a9 --- /dev/null +++ b/static/style.css @@ -0,0 +1,834 @@ +/* ======================================== + SeismoSense - Professional Dark Theme + ======================================== */ + +:root { + /* Dark Theme Colors */ + --bg-primary: #0a0e14; + --bg-secondary: #111820; + --bg-tertiary: #1a222d; + --bg-card: #151c26; + --bg-input: #0d1218; + + /* Accent Colors */ + --accent: #00d4aa; + --accent-hover: #00f5c4; + --accent-muted: #00a88a; + + /* Text Colors */ + --text-primary: #e6edf3; + --text-secondary: #8b949e; + --text-muted: #6e7681; + + /* Alert Colors */ + --green: #22c55e; + --green-bg: rgba(34, 197, 94, 0.15); + --orange: #f97316; + --orange-bg: rgba(249, 115, 22, 0.15); + --red: #ef4444; + --red-bg: rgba(239, 68, 68, 0.15); + --yellow: #eab308; + --yellow-bg: rgba(234, 179, 8, 0.15); + + /* UI Elements */ + --border: #30363d; + --border-focus: var(--accent); + + /* Shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 30px rgba(0, 212, 170, 0.15); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + line-height: 1.6; +} + +/* Background Pattern */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + radial-gradient(ellipse at 20% 20%, rgba(0, 212, 170, 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 80% 80%, rgba(0, 212, 170, 0.05) 0%, transparent 50%), + repeating-linear-gradient( + 0deg, + transparent, + transparent 50px, + rgba(0, 212, 170, 0.02) 50px, + rgba(0, 212, 170, 0.02) 51px + ), + repeating-linear-gradient( + 90deg, + transparent, + transparent 50px, + rgba(0, 212, 170, 0.02) 50px, + rgba(0, 212, 170, 0.02) 51px + ); + pointer-events: none; + z-index: -1; +} + +/* Header */ +.header { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + padding: 0 2rem; + position: sticky; + top: 0; + z-index: 100; + backdrop-filter: blur(10px); +} + +.header-content { + max-width: 1400px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + height: 70px; +} + +.logo { + display: flex; + align-items: center; + gap: 0; +} + +.logo-text { + font-size: 1.5rem; + font-weight: 700; + background: linear-gradient(135deg, var(--accent), #00f5c4); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Navigation Tabs */ +.nav-tabs { + display: flex; + gap: 0.5rem; +} + +.nav-tab { + padding: 0.625rem 1.25rem; + background: transparent; + border: none; + color: var(--text-secondary); + font-family: inherit; + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + border-radius: 8px; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.nav-tab:hover { + color: var(--text-primary); + background: var(--bg-tertiary); +} + +.nav-tab.active { + color: var(--accent); + background: rgba(0, 212, 170, 0.1); +} + +.nav-tab i { + font-size: 0.85rem; +} + +/* Main Content */ +.main-content { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; +} + +/* Tab Panels */ +.tab-panel { + display: none; + animation: fadeIn 0.3s ease; +} + +.tab-panel.active { + display: block; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Dashboard Grid */ +.dashboard-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; +} + +@media (max-width: 1024px) { + .dashboard-grid { + grid-template-columns: 1fr; + } +} + +/* Cards */ +.card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 16px; + padding: 1.5rem; + box-shadow: var(--shadow-md); +} + +.card-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border); +} + +.card-title { + font-size: 1.1rem; + font-weight: 600; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.card-title i { + color: var(--accent); +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.25rem; +} + +.form-label { + display: block; + font-size: 0.85rem; + font-weight: 500; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.form-label .required { + color: var(--red); +} + +.form-input { + width: 100%; + padding: 0.875rem 1rem; + background: var(--bg-input); + border: 1px solid var(--border); + border-radius: 10px; + color: var(--text-primary); + font-family: 'JetBrains Mono', monospace; + font-size: 0.95rem; + transition: all 0.2s ease; +} + +.form-input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(0, 212, 170, 0.15); +} + +.form-input::placeholder { + color: var(--text-muted); +} + +.form-help { + font-size: 0.75rem; + color: var(--text-muted); + margin-top: 0.375rem; +} + +/* Button */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.875rem 1.5rem; + background: linear-gradient(135deg, var(--accent), var(--accent-muted)); + color: var(--bg-primary); + font-family: inherit; + font-size: 0.95rem; + font-weight: 600; + border: none; + border-radius: 10px; + cursor: pointer; + transition: all 0.2s ease; + width: 100%; + margin-top: 0.5rem; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-glow); +} + +.btn:active { + transform: translateY(0); +} + +.btn:disabled { + opacity: 0.7; + cursor: not-allowed; + transform: none; +} + +.btn i { + font-size: 1rem; +} + +/* Button row: two buttons side by side */ +.btn-row { + display: flex; + gap: 0.75rem; + margin-top: 0.5rem; +} + +.btn-row .btn { + margin-top: 0; + flex: 1; +} + +/* Secondary / ghost button (Clear) */ +.btn-secondary { + background: transparent; + color: var(--text-secondary); + border: 1px solid var(--border); +} + +.btn-secondary:hover { + background: var(--bg-tertiary); + color: var(--text-primary); + border-color: var(--text-secondary); + box-shadow: none; +} + + +/* Spinner */ +.spinner { + display: none; + width: 20px; + height: 20px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Result Display */ +.result-card { + margin-top: 1.5rem; + padding: 1.5rem; + border-radius: 12px; + animation: slideIn 0.4s ease; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.result-card.green { + background: var(--green-bg); + border: 1px solid var(--green); +} + +.result-card.orange { + background: var(--orange-bg); + border: 1px solid var(--orange); +} + +.result-card.red { + background: var(--red-bg); + border: 1px solid var(--red); +} + +.result-card.yellow { + background: var(--yellow-bg); + border: 1px solid var(--yellow); +} + +.result-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} + +.result-label { + font-size: 0.85rem; + font-weight: 500; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.result-value { + font-size: 1.5rem; + font-weight: 700; +} + +.result-card.green .result-value { color: var(--green); } +.result-card.orange .result-value { color: var(--orange); } +.result-card.red .result-value { color: var(--red); } +.result-card.yellow .result-value { color: var(--yellow); } + +.confidence-bar { + height: 8px; + background: var(--bg-tertiary); + border-radius: 4px; + overflow: hidden; + margin: 0.75rem 0; +} + +.confidence-fill { + height: 100%; + border-radius: 4px; + transition: width 1s cubic-bezier(0.4, 0, 0.2, 1); +} + +.confidence-fill.green { background: var(--green); } +.confidence-fill.orange { background: var(--orange); } +.confidence-fill.red { background: var(--red); } +.confidence-fill.yellow { background: var(--yellow); } + +.confidence-text { + font-size: 0.8rem; + color: var(--text-secondary); +} + +.confidence-text span { + font-family: 'JetBrains Mono', monospace; + font-weight: 600; + color: var(--text-primary); +} + +/* Stats Grid */ +.stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-bottom: 1.5rem; +} + +.stat-item { + background: var(--bg-tertiary); + border-radius: 10px; + padding: 1rem; + text-align: center; +} + +.stat-value { + font-size: 1.75rem; + font-weight: 700; + color: var(--accent); + font-family: 'JetBrains Mono', monospace; +} + +.stat-label { + font-size: 0.75rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-top: 0.25rem; +} + +/* Confusion Matrix */ +.conf-matrix-container { + text-align: center; + padding: 1rem; +} + +.conf-matrix-img { + max-width: 100%; + height: auto; + border-radius: 8px; + box-shadow: var(--shadow-md); +} + +/* Alert Level Cards */ +.alert-levels { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-top: 1.5rem; +} + +.alert-level { + padding: 1rem; + border-radius: 10px; + text-align: center; +} + +.alert-level.green { + background: var(--green-bg); + border: 1px solid var(--green); +} + +.alert-level.orange { + background: var(--orange-bg); + border: 1px solid var(--orange); +} + +.alert-level.red { + background: var(--red-bg); + border: 1px solid var(--red); +} + +.alert-level.yellow { + background: var(--yellow-bg); + border: 1px solid var(--yellow); +} + +.alert-level-icon { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} + +.alert-level.green .alert-level-icon { color: var(--green); } +.alert-level.orange .alert-level-icon { color: var(--orange); } +.alert-level.red .alert-level-icon { color: var(--red); } +.alert-level.yellow .alert-level-icon { color: var(--yellow); } + +.alert-level-name { + font-weight: 600; + font-size: 0.9rem; +} + +.alert-level.green .alert-level-name { color: var(--green); } +.alert-level.orange .alert-level-name { color: var(--orange); } +.alert-level.red .alert-level-name { color: var(--red); } +.alert-level.yellow .alert-level-name { color: var(--yellow); } + +.alert-level-desc { + font-size: 0.75rem; + color: var(--text-muted); + margin-top: 0.25rem; +} + +/* About Section */ +.about-content { + max-width: 800px; + margin: 0 auto; +} + +.about-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 16px; + padding: 2rem; + margin-bottom: 1.5rem; +} + +.about-title { + font-size: 1.25rem; + font-weight: 600; + color: var(--accent); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.about-text { + color: var(--text-secondary); + font-size: 0.95rem; + line-height: 1.7; +} + +.feature-list { + list-style: none; + margin-top: 1rem; +} + +.feature-list li { + padding: 0.5rem 0; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 0.75rem; +} + +.feature-list li i { + color: var(--accent); + font-size: 0.85rem; +} + +.tech-stack { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 1rem; +} + +.tech-badge { + padding: 0.375rem 0.75rem; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + font-size: 0.8rem; + color: var(--text-secondary); +} + +/* Footer */ +.footer { + background: var(--bg-secondary); + border-top: 1px solid var(--border); + padding: 0; + margin-top: 2rem; +} + +.footer-content { + max-width: 1400px; + margin: 0 auto; + padding: 1.5rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; +} + +.footer-brand { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.95rem; + color: var(--text-secondary); +} + +.footer-brand span:first-child { + font-weight: 600; + color: var(--accent); +} + +.footer-divider { + color: var(--text-muted); +} + +.footer-links { + display: flex; + gap: 1rem; +} + +.footer-link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + color: var(--text-secondary); + text-decoration: none; + font-size: 0.9rem; + transition: color 0.2s ease; +} + +.footer-link svg { + width: 18px; + height: 18px; +} + +.footer-link:hover { + color: var(--accent); +} + +.footer-copyright { + font-size: 0.85rem; + color: var(--text-muted); +} + +.footer-link-inline { + color: var(--accent); + text-decoration: none; + font-weight: 500; +} + +.footer-link-inline:hover { + text-decoration: underline; +} + +/* Error State */ +.error-message { + background: var(--red-bg); + border: 1px solid var(--red); + border-radius: 10px; + padding: 1rem; + color: var(--red); + font-size: 0.9rem; + margin-top: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +/* Empty Result State */ +.empty-result { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; + text-align: center; +} + +.empty-icon { + width: 80px; + height: 80px; + background: var(--bg-tertiary); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1rem; +} + +.empty-icon i { + font-size: 2rem; + color: var(--accent); + opacity: 0.5; +} + +.empty-text { + color: var(--text-muted); + font-size: 0.9rem; +} + +/* Toast Notifications */ +.toast-container { + position: fixed; + top: 90px; + right: 20px; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.toast { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 1.25rem; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 12px; + animation: toast-in 0.3s ease; + max-width: 350px; + box-shadow: var(--shadow-lg); +} + +.toast.toast-out { + animation: toast-out 0.3s ease forwards; +} + +@keyframes toast-in { + from { opacity: 0; transform: translateX(100%); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes toast-out { + from { opacity: 1; transform: translateX(0); } + to { opacity: 0; transform: translateX(100%); } +} + +.toast-icon { + width: 24px; + height: 24px; + flex-shrink: 0; +} + +.toast.success .toast-icon { color: var(--green); } +.toast.error .toast-icon { color: var(--red); } +.toast.warning .toast-icon { color: var(--yellow); } +.toast.info .toast-icon { color: var(--accent); } + +.toast-message { + font-size: 0.9rem; + color: var(--text-primary); +} + +/* Responsive */ +@media (max-width: 768px) { + .header { + padding: 0 1rem; + } + + .header-content { + flex-direction: column; + height: auto; + padding: 1rem 0; + gap: 1rem; + } + + .nav-tabs { + width: 100%; + justify-content: center; + } + + .nav-tab { + padding: 0.5rem 0.75rem; + font-size: 0.8rem; + } + + .nav-tab span { + display: none; + } + + .main-content { + padding: 1rem; + } + + .alert-levels { + grid-template-columns: 1fr; + } + + .footer-content { + flex-direction: column; + text-align: center; + } + + .toast-container { + left: 1rem; + right: 1rem; + } + + .toast { + max-width: 100%; + } +} diff --git a/templates/index.html b/templates/index.html index 82cf5da..0885071 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,331 +5,381 @@ SeismoSense – AI Earthquake Alert Classifier - - + + + - - - +
+ + +

Community Decimal Intensity (0-10)

+
-
-
-
-
+
+ + +

Modified Mercalli Intensity (0-10)

+
-
- -

AI-Powered Earthquake Alert Classifier

+
+ + +

Significance parameter (0-1000)

+
-
-
- - - Typical range: 0.0 – 9.0 -
+
+ + +
+
+
-
- - + +
+
+

+ + Prediction Result +

+
+ + {% if result and result != 'Error' %} +
+
+ Predicted Alert + {{ result | upper }} +
+ {% if confidence %} +
+
+
+

+ Confidence: {{ confidence }}% +

+ {% endif %} +
+ {% elif result and result.startswith('Error') %} +
+ + {{ result }} +
+ {% else %} +
+
+ +
+

Enter earthquake parameters and click "Analyze & Predict"

+
+ {% endif %} + + +
+

+ Alert Levels Guide +

+

+ SeismoSense classifies earthquakes into four alert levels based on multiple seismic parameters: +

+
+
+
+ +
+
Green (Low)
+
Minor seismic activity, minimal risk
+
+ +
+
+ +
+
Orange (Moderate)
+
Moderate earthquake, some damage possible
+
+ +
+
+ +
+
Red (High)
+
Major earthquake, significant damage expected
+
+ +
+
+ +
+
Yellow (Watch)
+
Elevated activity, potential for escalation
+
+
+
+ + +
+

+ Quick Tip +

+

+ For accurate predictions, use data from reliable seismic monitoring stations. + CDI and MMI values are typically derived from crowd-sourced reports and instrumental readings. +

+
+
+
-
- - + +
+
+ +
+
+

+ + Model Performance: Confusion Matrix +

+
+ +
+ Confusion Matrix +
+ +
+
+
4
+
Alert Classes
+
+
+
5
+
Input Features
+
+
+
+ + +
+
+

+ + Reading the Matrix +

+
+ +
+

+ The confusion matrix shows how well the model classifies earthquake alerts: +

+
    +
  • + + Diagonal values — Correct predictions +
  • +
  • + + Off-diagonal values — Misclassifications +
  • +
  • + + Rows — True actual labels +
  • +
  • + + Columns — Predicted labels +
  • +
+
+
+
-
- - + +
+
+
+

+ + About SeismoSense +

+

+ SeismoSense is an AI-powered earthquake alert classifier that uses machine learning + to analyze seismic parameters and predict alert levels. The system processes multiple + input features including magnitude, depth, CDI (Community Decimal Intensity), MMI + (Modified Mercalli Intensity), and SIG (Significance) to classify earthquakes + into four alert categories. +

+
+ +
+

+ + Features +

+
    +
  • + + Real-time earthquake alert prediction +
  • +
  • + + Machine learning-powered classification +
  • +
  • + + Model performance visualization +
  • +
  • + + Responsive design for all devices +
  • +
  • + + Reliable error handling +
  • +
+
+ +
+

+ + Technology Stack +

+
+ Python + Flask + Scikit-learn + Pandas + NumPy + Seaborn + Matplotlib + HTML5 + CSS3 + Font Awesome +
+
+ +
+

+ + Credits +

+

+ Built by Sakib Hossain. Licensed under the MIT License.
+ View the source code on GitHub. +

+
+
-
- - -
+ - - + +
- -