Add data_visualization/js/visualization.js
This commit is contained in:
parent
083b4caa74
commit
8f146dadc3
1 changed files with 136 additions and 0 deletions
136
data_visualization/js/visualization.js
Normal file
136
data_visualization/js/visualization.js
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/**
|
||||||
|
* visualization.js
|
||||||
|
* Verantwortlich für Rendering der Diagramme und Aktualisierung bei neuen Daten.
|
||||||
|
* Part of experiment: resonanzband_analysis (artifact: data_visualization)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert die aggregierten Kennzahlen im summary-section.
|
||||||
|
* @param {Array<Object>} data - Analyseergebnisse mit Feldern { run_id, band_center, band_width_p10_p90, max_outlier, overhead_ms }
|
||||||
|
*/
|
||||||
|
export function updateSummary(data) {
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
const summaryEl = document.getElementById('summary-section');
|
||||||
|
if (summaryEl) summaryEl.textContent = 'Keine Daten verfügbar.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const avg = (arr, key) => arr.reduce((sum, d) => sum + (d[key] ?? 0), 0) / arr.length;
|
||||||
|
const maxOutlier = Math.max(...data.map(d => d.max_outlier ?? 0));
|
||||||
|
|
||||||
|
const summary = {
|
||||||
|
avgBandCenter: avg(data, 'band_center').toFixed(2),
|
||||||
|
avgWidth: avg(data, 'band_width_p10_p90').toFixed(2),
|
||||||
|
maxOutlier,
|
||||||
|
avgOverhead: avg(data, 'overhead_ms').toFixed(2)
|
||||||
|
};
|
||||||
|
|
||||||
|
const summaryEl = document.getElementById('summary-section');
|
||||||
|
if (!summaryEl) return;
|
||||||
|
summaryEl.innerHTML = `
|
||||||
|
<div class="summary__metric"><span class="summary__label">Durchschnitt Bandcenter:</span> <span class="summary__value">${summary.avgBandCenter}</span></div>
|
||||||
|
<div class="summary__metric"><span class="summary__label">Durchschnitt Bandbreite (P10–P90):</span> <span class="summary__value">${summary.avgWidth}</span></div>
|
||||||
|
<div class="summary__metric"><span class="summary__label">Maximaler Ausreißer:</span> <span class="summary__value">${summary.maxOutlier}</span></div>
|
||||||
|
<div class="summary__metric"><span class="summary__label">Mittlerer Overhead (ms):</span> <span class="summary__value">${summary.avgOverhead}</span></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet Diagramme und Statistiken im charts-container basierend auf Analyseergebnissen.
|
||||||
|
* @param {Array<Object>} results - Analyseergebnisse
|
||||||
|
*/
|
||||||
|
export function renderResults(results) {
|
||||||
|
const container = document.getElementById('charts-container');
|
||||||
|
if (!container) return;
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(results) || results.length === 0) {
|
||||||
|
container.textContent = 'Keine Ergebnisse gefunden.';
|
||||||
|
updateSummary([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagramm mit Canvas-Element zeichnen (native Lösung ohne externe Lib)
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = container.clientWidth;
|
||||||
|
canvas.height = 300;
|
||||||
|
canvas.setAttribute('role', 'img');
|
||||||
|
canvas.setAttribute('aria-label', 'Diagramm der Resonanzband-Metriken');
|
||||||
|
container.appendChild(canvas);
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const padding = 40;
|
||||||
|
const width = canvas.width - 2 * padding;
|
||||||
|
const height = canvas.height - 2 * padding;
|
||||||
|
|
||||||
|
const maxBand = Math.max(...results.map(r => r.band_center + r.band_width_p10_p90));
|
||||||
|
const minBand = Math.min(...results.map(r => r.band_center - r.band_width_p10_p90));
|
||||||
|
|
||||||
|
const scaleY = val => height - ((val - minBand) / (maxBand - minBand)) * height + padding;
|
||||||
|
const scaleX = (i) => padding + (i / (results.length - 1)) * width;
|
||||||
|
|
||||||
|
// Achsen zeichnen
|
||||||
|
ctx.strokeStyle = '#333';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(padding, padding);
|
||||||
|
ctx.lineTo(padding, height + padding);
|
||||||
|
ctx.lineTo(width + padding, height + padding);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Linien für Bandcenter zeichnen
|
||||||
|
ctx.strokeStyle = '#0057b7';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
results.forEach((r, i) => {
|
||||||
|
const x = scaleX(i);
|
||||||
|
const y = scaleY(r.band_center);
|
||||||
|
if (i === 0) ctx.moveTo(x, y);
|
||||||
|
else ctx.lineTo(x, y);
|
||||||
|
});
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Outlier Punkte zeichnen
|
||||||
|
ctx.fillStyle = '#b70000';
|
||||||
|
results.forEach((r, i) => {
|
||||||
|
const x = scaleX(i);
|
||||||
|
const y = scaleY(r.band_center + r.max_outlier);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 3, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tooltips: einfache textuelle Version
|
||||||
|
canvas.addEventListener('mousemove', e => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
const idx = Math.round((x - padding) / width * (results.length - 1));
|
||||||
|
const item = results[idx];
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
const tooltip = document.getElementById('chart-tooltip') ?? document.createElement('div');
|
||||||
|
tooltip.id = 'chart-tooltip';
|
||||||
|
tooltip.className = 'chart__tooltip';
|
||||||
|
tooltip.style.position = 'absolute';
|
||||||
|
tooltip.style.left = `${x + 10}px`;
|
||||||
|
tooltip.style.top = `${y + 10}px`;
|
||||||
|
tooltip.style.background = '#222';
|
||||||
|
tooltip.style.color = '#fff';
|
||||||
|
tooltip.style.padding = '4px 8px';
|
||||||
|
tooltip.style.borderRadius = '4px';
|
||||||
|
tooltip.style.fontSize = '0.8rem';
|
||||||
|
tooltip.style.pointerEvents = 'none';
|
||||||
|
tooltip.textContent = `Run ${item.run_id}: Center=${item.band_center}, Width=${item.band_width_p10_p90}, Outlier=${item.max_outlier}`;
|
||||||
|
|
||||||
|
document.body.appendChild(tooltip);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener('mouseleave', () => {
|
||||||
|
const tooltip = document.getElementById('chart-tooltip');
|
||||||
|
if (tooltip) tooltip.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Zusammenfassung aktualisieren
|
||||||
|
updateSummary(results);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue