cams-pollution-dashboard / templates /aurora_variables.html
aditya-me13's picture
Integration of Aurora
4f0125c
raw
history blame
16.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aurora Prediction Variables - CAMS Air Pollution</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
h2 { color: #34495e; border-bottom: 2px solid #9b59b6; padding-bottom: 10px; }
.method-section, .form-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #9b59b6;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #2c3e50;
}
select {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
select:focus {
border-color: #9b59b6;
outline: none;
}
.btn {
background: #9b59b6;
color: white;
padding: 12px 24px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
}
.btn:hover { background: #8e44ad; }
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.btn-secondary {
background: #6c757d;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-success {
background: #28a745;
}
.btn-success:hover {
background: #218838;
}
.step-selector {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.step-btn {
padding: 8px 16px;
border: 2px solid #ddd;
background: white;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.step-btn:hover {
border-color: #9b59b6;
}
.step-btn.active {
background: #9b59b6;
color: white;
border-color: #9b59b6;
}
.info-box {
background: #e8f4f8;
border: 1px solid #bee5eb;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
}
.hidden-section {
display: none;
}
.loading {
text-align: center;
color: #666;
font-style: italic;
}
.back-link {
color: #9b59b6;
text-decoration: none;
font-weight: 600;
margin-bottom: 20px;
display: inline-block;
}
.back-link:hover {
text-decoration: underline;
}
.color-preview-section {
margin-top: 15px;
}
.color-gradient {
width: 100%;
height: 20px;
border-radius: 5px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<div class="container">
<a href="{{ url_for('prediction_runs') }}" class="back-link">← Back to Prediction Runs</a>
<h1>🔮 Aurora Prediction Variables</h1>
<div class="info-box">
<strong>📁 Run Directory:</strong> {{ run_dir }}<br>
<strong>📊 Steps Available:</strong> {{ steps_data|length }} ({{ (steps_data|length * 12) }}h coverage)
</div>
<!-- Step 1: Step Selection -->
<div class="method-section">
<h2>⏰ Step 1: Select Prediction Step</h2>
<p>Choose which prediction time step to analyze:</p>
<div class="step-selector">
{% for step_data in steps_data %}
<div class="step-btn" onclick="selectStep({{ step_data.step }}, '{{ step_data.filename }}')">
Step {{ step_data.step }}<br>
<small>T+{{ step_data.forecast_hours }}h</small>
</div>
{% endfor %}
</div>
</div>
<!-- Step 2: Variable Selection (hidden until step selected) -->
<div id="variableSection" class="hidden-section">
<div class="form-section">
<h2>🧪 Step 2: Select Variable</h2>
<div id="variableLoading" class="loading">Loading variables...</div>
<div id="variableContent" class="hidden-section">
<form method="POST" action="{{ url_for('aurora_plot') }}" id="plotForm">
<input type="hidden" name="run_dir" value="{{ run_dir }}">
<input type="hidden" name="step" id="selected_step" value="">
<div class="form-group">
<label for="variable">Choose Variable:</label>
<select name="variable" id="variable" required onchange="handleVariableChange()">
<option value="">-- Select a variable --</option>
</select>
</div>
<!-- Step 3: Pressure Level (shown for atmospheric variables) -->
<div id="pressureSection" class="hidden-section">
<h2>📊 Step 3: Select Pressure Level</h2>
<div class="form-group">
<label for="pressure_level">Pressure Level (hPa):</label>
<select name="pressure_level" id="pressure_level">
</select>
</div>
</div>
<!-- Step 4: Color Theme Selection -->
<div id="plotOptionsSection" class="hidden-section">
<h2>🎨 Step 4: Select Color Theme</h2>
<div class="form-group">
<label for="color_theme">Select Color Scheme:</label>
<select name="color_theme" id="color_theme" onchange="updateColorPreview()">
{% for theme_id, theme_name in color_themes.items() %}
<option value="{{ theme_id }}"
{% if theme_id == 'viridis' %}selected{% endif %}>
{{ theme_name }}
</option>
{% endfor %}
</select>
</div>
<div class="color-preview-section">
<p><strong>Preview:</strong> <span id="colorPreviewText">Viridis</span></p>
<div class="color-gradient" id="colorPreview"></div>
</div>
</div>
<!-- Step 5: Generate Plot -->
<h2>📈 Step 5: Generate Plot</h2>
<button type="submit" name="plot_type" value="static" class="btn" id="staticPlotBtn" disabled>
📊 Generate Static Plot
</button>
<button type="submit" name="plot_type" value="interactive" class="btn btn-success" id="interactivePlotBtn" disabled>
🌐 Generate Interactive Plot
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Download Section -->
<div class="method-section">
<h2>💾 Download Data</h2>
<a href="{{ url_for('download_prediction_netcdf', filename=run_dir) }}" class="btn btn-secondary">
📥 Download All Files
</a>
</div>
</div>
<script>
let currentStep = null;
let currentVariables = null;
function selectStep(step, filename) {
// Update UI
document.querySelectorAll('.step-btn').forEach(btn => btn.classList.remove('active'));
event.target.closest('.step-btn').classList.add('active');
currentStep = step;
document.getElementById('selected_step').value = step;
// Show variable section and loading
document.getElementById('variableSection').classList.remove('hidden-section');
document.getElementById('variableLoading').style.display = 'block';
document.getElementById('variableContent').classList.add('hidden-section');
// Load variables for this step
fetch(`/api/aurora_step_variables/{{ run_dir }}/${step}`)
.then(response => response.json())
.then(data => {
if (data.error) {
alert('Error loading variables: ' + data.error);
return;
}
currentVariables = data;
populateVariables(data);
// Hide loading, show content
document.getElementById('variableLoading').style.display = 'none';
document.getElementById('variableContent').classList.remove('hidden-section');
})
.catch(error => {
console.error('Error:', error);
alert('Error loading variables');
document.getElementById('variableLoading').innerHTML = 'Error loading variables';
});
}
function populateVariables(data) {
const select = document.getElementById('variable');
select.innerHTML = '<option value="">-- Select a variable --</option>';
if (data.surface_vars && data.surface_vars.length > 0) {
const surfaceGroup = document.createElement('optgroup');
surfaceGroup.label = 'Surface Variables';
data.surface_vars.forEach(varName => {
const option = document.createElement('option');
option.value = varName;
option.textContent = `${varName} (Surface)`;
option.dataset.type = 'surface';
surfaceGroup.appendChild(option);
});
select.appendChild(surfaceGroup);
}
if (data.atmos_vars && data.atmos_vars.length > 0) {
const atmosGroup = document.createElement('optgroup');
atmosGroup.label = 'Atmospheric Variables';
data.atmos_vars.forEach(varName => {
const option = document.createElement('option');
option.value = varName;
option.textContent = `${varName} (Atmospheric)`;
option.dataset.type = 'atmospheric';
atmosGroup.appendChild(option);
});
select.appendChild(atmosGroup);
}
// Populate pressure levels
const pressureSelect = document.getElementById('pressure_level');
pressureSelect.innerHTML = '';
if (data.pressure_levels && data.pressure_levels.length > 0) {
data.pressure_levels.forEach(level => {
const option = document.createElement('option');
option.value = level;
option.textContent = `${level} hPa`;
pressureSelect.appendChild(option);
});
}
}
function handleVariableChange() {
const select = document.getElementById('variable');
const selectedOption = select.options[select.selectedIndex];
if (selectedOption.value) {
const isAtmospheric = selectedOption.dataset.type === 'atmospheric';
// Show/hide pressure section
const pressureSection = document.getElementById('pressureSection');
if (isAtmospheric) {
pressureSection.classList.remove('hidden-section');
} else {
pressureSection.classList.add('hidden-section');
}
// Show plot options
document.getElementById('plotOptionsSection').classList.remove('hidden-section');
// Initialize color preview when plot options are shown
updateColorPreview();
// Enable plot buttons
document.getElementById('staticPlotBtn').disabled = false;
document.getElementById('interactivePlotBtn').disabled = false;
} else {
// Hide sections if no variable selected
document.getElementById('pressureSection').classList.add('hidden-section');
document.getElementById('plotOptionsSection').classList.add('hidden-section');
// Disable plot buttons
document.getElementById('staticPlotBtn').disabled = true;
document.getElementById('interactivePlotBtn').disabled = true;
}
}
// Color theme preview
const colorMaps = {
'viridis': 'linear-gradient(to right, #440154, #414487, #2a788e, #22a884, #7ad151, #fde725)',
'plasma': 'linear-gradient(to right, #0d0887, #6a00a8, #b12a90, #e16462, #fca636, #f0f921)',
'YlOrRd': 'linear-gradient(to right, #ffffcc, #ffeda0, #fed976, #feb24c, #fd8d3c, #e31a1c)',
'Blues': 'linear-gradient(to right, #f7fbff, #deebf7, #c6dbef, #9ecae1, #6baed6, #2171b5)',
'Reds': 'linear-gradient(to right, #fff5f0, #fee0d2, #fcbba1, #fc9272, #fb6a4a, #de2d26)',
'Greens': 'linear-gradient(to right, #f7fcf5, #e5f5e0, #c7e9c0, #a1d99b, #74c476, #238b45)',
'Oranges': 'linear-gradient(to right, #fff5eb, #fee6ce, #fdd0a2, #fdae6b, #fd8d3c, #d94701)',
'Purples': 'linear-gradient(to right, #fcfbfd, #efedf5, #dadaeb, #bcbddc, #9e9ac8, #756bb1)',
'inferno': 'linear-gradient(to right, #000004, #420a68, #932667, #dd513a, #fca50a, #fcffa4)',
'magma': 'linear-gradient(to right, #000004, #3b0f70, #8c2981, #de4968, #fe9f6d, #fcfdbf)',
'cividis': 'linear-gradient(to right, #00224e, #123570, #3b496c, #575d6d, #707173, #8a8678)',
'coolwarm': 'linear-gradient(to right, #3b4cc0, #688aef, #b7d4f1, #f7f7f7, #f4b2a6, #dc7176, #a50026)',
'RdYlBu': 'linear-gradient(to right, #a50026, #d73027, #f46d43, #fdae61, #fee090, #e0f3f8, #abd9e9, #74add1, #4575b4, #313695)',
'Spectral': 'linear-gradient(to right, #9e0142, #d53e4f, #f46d43, #fdae61, #fee08b, #e6f598, #abdda4, #66c2a5, #3288bd, #5e4fa2)'
};
function updateColorPreview() {
const theme = document.getElementById('color_theme').value;
const preview = document.getElementById('colorPreview');
const previewText = document.getElementById('colorPreviewText');
previewText.textContent = document.getElementById('color_theme').selectedOptions[0].text;
if (colorMaps[theme]) {
preview.style.background = colorMaps[theme];
} else {
preview.style.background = colorMaps['viridis'];
}
}
// Initialize color preview when page loads
document.addEventListener('DOMContentLoaded', function() {
updateColorPreview();
});
</script>
</body>
</html>