From a7b467bf3ab3ed8d462557edfe67da6fe6c19147 Mon Sep 17 00:00:00 2001 From: Mika Date: Wed, 18 Mar 2026 13:17:47 +0000 Subject: [PATCH] Add heatmap_visualization/js/app.js --- heatmap_visualization/js/app.js | 125 ++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 heatmap_visualization/js/app.js diff --git a/heatmap_visualization/js/app.js b/heatmap_visualization/js/app.js new file mode 100644 index 0000000..ecf127e --- /dev/null +++ b/heatmap_visualization/js/app.js @@ -0,0 +1,125 @@ +'use strict'; + +/** + * @file js/app.js + * @description Initialisiert die Resonanzband-Heatmap-Anwendung. Lädt Daten von /heatmap und reagiert auf Nutzerinteraktionen. + * @version 1.0.0 + */ + +/** + * Lädt Heatmap-Daten von der API /heatmap. + * @param {Object} [filters] - optionale Filterparameter { cohort_id, time_window } + * @returns {Promise} - Promise mit Heatmap-Daten und Metadaten + */ +async function loadHeatmapData(filters = {}) { + try { + const url = new URL('/heatmap', window.location.origin); + Object.entries(filters).forEach(([key, value]) => { + if (value !== undefined && value !== '') { + url.searchParams.append(key, value); + } + }); + const response = await fetch(url.toString(), { method: 'GET' }); + if (!response.ok) { + throw new Error(`API-Fehler: ${response.status}`); + } + const data = await response.json(); + renderHeatmap(data); + } catch (err) { + console.error('Fehler beim Laden der Heatmap-Daten:', err); + renderError(`Fehler beim Laden der Daten: ${err.message}`); + } +} + +/** + * Rendert die Heatmap auf Basis der übergebenen Daten. + * @param {Object} data - Heatmap-Datenobjekt + */ +function renderHeatmap(data) { + const canvas = document.getElementById('heatmap-canvas'); + if (!canvas) return; + const ctx = canvas.getContext('2d'); + const matrix = data?.matrix || []; + + const rows = matrix.length; + const cols = rows > 0 ? matrix[0].length : 0; + const cellWidth = canvas.width / (cols || 1); + const cellHeight = canvas.height / (rows || 1); + + ctx.clearRect(0, 0, canvas.width, canvas.height); + matrix.forEach((row, y) => { + row.forEach((value, x) => { + const color = getColorForValue(value); + ctx.fillStyle = color; + ctx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); + }); + }); + renderMetrics(data); +} + +/** + * Zeigt grundlegende Metriken aus den Heatmap-Daten an. + * @param {Object} data - Heatmap-Datenobjekt mit Metadaten + */ +function renderMetrics(data) { + const metricsContainer = document.getElementById('heatmap-metrics'); + if (!metricsContainer) return; + const { band_center, band_width, cluster_score } = data || {}; + metricsContainer.innerHTML = ` +

Band Center: ${band_center ?? '–'}

+

Band Width: ${band_width ?? '–'}

+

Cluster Score: ${cluster_score ?? '–'}

+ `; +} + +/** + * Berechnet eine Farbskala für Heatmap-Werte. + * @param {number} value - numerischer Intensitätswert (0–1) + * @returns {string} CSS-Farbwert (rgba) + */ +function getColorForValue(value) { + const v = Math.max(0, Math.min(1, value)); + const r = Math.floor(255 * v); + const g = Math.floor(100 * (1 - v)); + const b = 150; + return `rgba(${r}, ${g}, ${b}, 0.9)`; +} + +/** + * Zeigt eine Fehlermeldung im UI an. + * @param {string} message - Text der Fehlermeldung + */ +function renderError(message) { + const container = document.getElementById('heatmap-container'); + if (container) container.innerHTML = `
${message}
`; +} + +/** + * Behandelt Änderungen der Filter und ruft neue Daten ab. + * @param {Event} event - Auslösendes Event + */ +function handleFilterChange(event) { + const form = event.currentTarget.form || document.getElementById('filter-form'); + if (!form) return; + + const formData = new FormData(form); + const filters = Object.fromEntries(formData.entries()); + loadHeatmapData(filters); +} + +/** + * Initialisiert die App, bindet Event-Listener und lädt erste Heatmap-Daten. + */ +function initApp() { + const form = document.getElementById('filter-form'); + if (form) { + form.addEventListener('change', handleFilterChange); + form.addEventListener('submit', (e) => { + e.preventDefault(); + handleFilterChange(e); + }); + } + loadHeatmapData(); +} + +window.addEventListener('load', initApp);