Add data_visualization/js/ui.js

This commit is contained in:
Mika 2026-03-29 03:07:25 +00:00
parent e77e3f1a54
commit 8f80a64b21

111
data_visualization/js/ui.js Normal file
View file

@ -0,0 +1,111 @@
"use strict";
/**
* UI Logic for Data Visualization
* Handles rendering charts, updating stats, and applying filters.
*/
import { fetchData } from './api.js';
/**
* @typedef {Object} SensorData
* @property {string} timestamp - ISO time string
* @property {number} intensity - Measured light intensity
* @property {number} wavelength - Light wavelength in nm
* @property {string} sensor_id - Identifier of the sensor
*/
/**
* Renders chart visualization inside the DOM element with id 'chart-container'.
* @param {SensorData[]} dataArray
*/
export function renderChart(dataArray) {
const container = document.getElementById('chart-container');
if (!container) return;
container.innerHTML = '';
// Create canvas for chart
const canvas = document.createElement('canvas');
canvas.setAttribute('role', 'img');
canvas.setAttribute('aria-label', 'Diagramm der Intensitätswerte über Zeit');
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
const timestamps = dataArray.map(d => new Date(d.timestamp).toLocaleTimeString());
const intensities = dataArray.map(d => d.intensity);
// Chart.js-like pseudo implementation (no external lib)
ctx.beginPath();
const maxIntensity = Math.max(...intensities, 1);
const minIntensity = Math.min(...intensities, 0);
const width = canvas.width = container.offsetWidth;
const height = canvas.height = Math.min(400, container.offsetHeight || 300);
ctx.strokeStyle = '#007acc';
ctx.lineWidth = 2;
intensities.forEach((value, i) => {
const x = (i / (intensities.length - 1)) * width;
const y = height - ((value - minIntensity) / (maxIntensity - minIntensity)) * height;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
updateSummary(dataArray);
}
/**
* Updates statistics panel with calculated summary data.
* @param {SensorData[]} dataArray
*/
export function updateSummary(dataArray) {
const avg = dataArray.length ? dataArray.reduce((acc, d) => acc + d.intensity, 0) / dataArray.length : 0;
const max = dataArray.length ? Math.max(...dataArray.map(d => d.intensity)) : 0;
const min = dataArray.length ? Math.min(...dataArray.map(d => d.intensity)) : 0;
const summaryEl = document.getElementById('summary-panel');
if (!summaryEl) return;
summaryEl.innerHTML = `
<div class="summary__item"><strong>Durchschnitt:</strong> ${avg.toFixed(2)}</div>
<div class="summary__item"><strong>Max:</strong> ${max.toFixed(2)}</div>
<div class="summary__item"><strong>Min:</strong> ${min.toFixed(2)}</div>
`;
}
/**
* Reads filter form values and triggers data reload.
*/
export async function applyFilters() {
const sensorSelect = document.getElementById('filter-sensor');
const timeStart = document.getElementById('filter-start');
const timeEnd = document.getElementById('filter-end');
const params = {};
if (sensorSelect?.value) params.sensor = sensorSelect.value;
if (timeStart?.value) params.start = timeStart.value;
if (timeEnd?.value) params.end = timeEnd.value;
const data = await fetchData(params).catch(() => []);
renderChart(data);
}
/**
* Binds UI event handlers.
*/
export function bindUIEvents() {
const form = document.getElementById('filter-form');
if (form) {
form.addEventListener('submit', e => {
e.preventDefault();
applyFilters();
});
}
}
// Auto-bind when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
bindUIEvents();
});