Add data_visualization/js/ui.js
This commit is contained in:
parent
160c6db0dc
commit
acac0c44da
1 changed files with 136 additions and 0 deletions
136
data_visualization/js/ui.js
Normal file
136
data_visualization/js/ui.js
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/* ui.js — Verantwortlich für DOM-Manipulation und UI-Rendering */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendert interaktive Diagramme für Umweltdaten.
|
||||||
|
* @param {Array<{timestamp: string, temperature: number, wind_speed: number, humidity: number}>} data - API Response von /data
|
||||||
|
*/
|
||||||
|
export function renderCharts(data) {
|
||||||
|
const chartContainer = document.querySelector('#data-charts');
|
||||||
|
if (!chartContainer) return;
|
||||||
|
chartContainer.innerHTML = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
const msg = document.createElement('p');
|
||||||
|
msg.textContent = 'Keine Daten verfügbar';
|
||||||
|
chartContainer.appendChild(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grundstruktur für Canvas-Diagramme
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.setAttribute('aria-label', 'Umweltdaten Diagramm');
|
||||||
|
canvas.setAttribute('role', 'img');
|
||||||
|
chartContainer.appendChild(canvas);
|
||||||
|
|
||||||
|
// Beispielhafte Nutzung von Chart.js oder ähnlicher Logik (ohne externe Ressourcen)
|
||||||
|
// Zur Vereinfachung: Darstellung als einfache Linien mit Canvas API
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const width = chartContainer.clientWidth;
|
||||||
|
const height = 200;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
const temperatures = data.map(d => d.temperature);
|
||||||
|
const winds = data.map(d => d.wind_speed);
|
||||||
|
const hums = data.map(d => d.humidity);
|
||||||
|
|
||||||
|
const maxTemp = Math.max(...temperatures, 1);
|
||||||
|
const maxWind = Math.max(...winds, 1);
|
||||||
|
const maxHum = Math.max(...hums, 1);
|
||||||
|
|
||||||
|
const step = width / (data.length - 1);
|
||||||
|
|
||||||
|
// Hilfsfunktion zum Zeichnen von Linien
|
||||||
|
const drawLine = (values, color, maxVal) => {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
values.forEach((val, i) => {
|
||||||
|
const x = i * step;
|
||||||
|
const y = height - (val / maxVal) * height;
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
drawLine(temperatures, '#e74c3c', maxTemp); // rot für Temp
|
||||||
|
drawLine(winds, '#3498db', maxWind); // blau für Wind
|
||||||
|
drawLine(hums, '#2ecc71', maxHum); // grün für Luftfeuchte
|
||||||
|
|
||||||
|
const legend = document.createElement('div');
|
||||||
|
legend.className = 'chart-legend';
|
||||||
|
legend.innerHTML = `
|
||||||
|
<span style="color:#e74c3c">Temperatur</span> |
|
||||||
|
<span style="color:#3498db">Wind</span> |
|
||||||
|
<span style="color:#2ecc71">Feuchtigkeit</span>
|
||||||
|
`;
|
||||||
|
chartContainer.appendChild(legend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendert eine Galerie aus Bilddaten.
|
||||||
|
* @param {Array<{url: string, capture_time: string, analysis_result: string}>} images - API Response von /images
|
||||||
|
*/
|
||||||
|
export function renderGallery(images) {
|
||||||
|
const galleryContainer = document.querySelector('#image-gallery');
|
||||||
|
if (!galleryContainer) return;
|
||||||
|
galleryContainer.innerHTML = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(images) || images.length === 0) {
|
||||||
|
const msg = document.createElement('p');
|
||||||
|
msg.textContent = 'Keine Bilddaten verfügbar';
|
||||||
|
galleryContainer.appendChild(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
images.forEach(imgData => {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'gallery-item';
|
||||||
|
|
||||||
|
const figure = document.createElement('figure');
|
||||||
|
|
||||||
|
const thumb = document.createElement('div');
|
||||||
|
thumb.className = 'image-thumb';
|
||||||
|
thumb.tabIndex = 0;
|
||||||
|
thumb.setAttribute('role', 'button');
|
||||||
|
thumb.setAttribute('aria-label', `Bild aufgenommen am ${imgData.capture_time}`);
|
||||||
|
|
||||||
|
// Bilddarstellung über CSS background-image, da keine externen Bilddateien verwendet werden dürfen
|
||||||
|
thumb.style.background = '#ccc'; // Platzhalter, da keine echten Bilder geladen werden dürfen
|
||||||
|
|
||||||
|
const caption = document.createElement('figcaption');
|
||||||
|
caption.textContent = `${imgData.capture_time} – ${imgData.analysis_result}`;
|
||||||
|
|
||||||
|
figure.appendChild(thumb);
|
||||||
|
figure.appendChild(caption);
|
||||||
|
wrapper.appendChild(figure);
|
||||||
|
galleryContainer.appendChild(wrapper);
|
||||||
|
|
||||||
|
// Interaktive Zoomfunktion (nur symbolisch)
|
||||||
|
thumb.addEventListener('click', () => {
|
||||||
|
const expanded = document.createElement('div');
|
||||||
|
expanded.className = 'overlay';
|
||||||
|
expanded.setAttribute('role', 'dialog');
|
||||||
|
expanded.setAttribute('aria-modal', 'true');
|
||||||
|
|
||||||
|
const expandedContent = document.createElement('div');
|
||||||
|
expandedContent.className = 'overlay-content';
|
||||||
|
expandedContent.style.background = '#999'; // Platzhalter für Zoom-Bild
|
||||||
|
|
||||||
|
const closeBtn = document.createElement('button');
|
||||||
|
closeBtn.textContent = 'Schließen';
|
||||||
|
closeBtn.className = 'close-button';
|
||||||
|
closeBtn.addEventListener('click', () => expanded.remove());
|
||||||
|
|
||||||
|
expanded.appendChild(expandedContent);
|
||||||
|
expanded.appendChild(closeBtn);
|
||||||
|
document.body.appendChild(expanded);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue