langzeitbelichtung_nachtfot.../data_visualization/js/app.js

144 lines
No EOL
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
/**
* Entry point for the night photography data visualization app.
* Coordinates fetching data, initializing chart & gallery, and handling filters.
*/
(async () => {
window.addEventListener('load', initApp);
/**
* Initializes the application by fetching data and rendering UI components.
* @returns {Promise<void>}
*/
async function initApp() {
const mainContainer = document.querySelector('main');
if (!mainContainer) return;
try {
const response = await fetch('/data');
if (!response.ok) throw new Error(`HTTP Error ${response.status}`);
const data = await response.json();
const { temperatures = [], images = [] } = data;
renderSummary(temperatures, images);
renderChart(temperatures);
renderGallery(images);
const filterControls = document.querySelector('.filter-controls');
if (filterControls) {
filterControls.addEventListener('input', handleFilterChange.bind(null, temperatures, images));
}
} catch (err) {
console.error('App initialization failed:', err);
showError(mainContainer, 'Fehler beim Laden der Daten.');
}
}
/**
* Handles changes in filter controls.
* @param {Array} temperatures
* @param {Array} images
* @param {Event} evt
*/
function handleFilterChange(temperatures, images, evt) {
const filterContainer = evt.currentTarget;
if (!filterContainer) return;
const timeRange = filterContainer.querySelector('[name="timeRange"]')?.value || '';
const minTemp = parseFloat(filterContainer.querySelector('[name="minTemp"]')?.value) || null;
const maxTemp = parseFloat(filterContainer.querySelector('[name="maxTemp"]')?.value) || null;
const filteredTemps = temperatures.filter(t => {
const valueOk = (!minTemp || t.value >= minTemp) && (!maxTemp || t.value <= maxTemp);
return valueOk;
});
const filteredImages = images.filter(i => {
const relatedTemp = temperatures.find(t => t.timestamp === i.timestamp);
if (!relatedTemp) return true;
return (!minTemp || relatedTemp.value >= minTemp) && (!maxTemp || relatedTemp.value <= maxTemp);
});
renderChart(filteredTemps);
renderGallery(filteredImages);
renderSummary(filteredTemps, filteredImages);
}
/**
* Renders a simple line chart (placeholder logic to be replaced by visualization module if available).
* @param {Array<{timestamp:string,value:number}>} temps
*/
function renderChart(temps) {
const chart = document.querySelector('#temperature-chart');
if (!chart) return;
chart.innerHTML = '';
const avg = temps.reduce((acc, t) => acc + t.value, 0) / (temps.length || 1);
const list = document.createElement('ul');
list.className = 'chart__list';
temps.forEach(t => {
const li = document.createElement('li');
li.textContent = `${new Date(t.timestamp).toLocaleTimeString()} ${t.value.toFixed(1)}°C`;
list.appendChild(li);
});
const avgDisplay = document.createElement('p');
avgDisplay.className = 'chart__avg';
avgDisplay.textContent = `Durchschnitt: ${avg.toFixed(1)}°C`;
chart.appendChild(list);
chart.appendChild(avgDisplay);
}
/**
* Renders an image gallery.
* @param {Array<{filename:string,timestamp:string,metadata:object}>} images
*/
function renderGallery(images) {
const gallery = document.querySelector('#image-gallery');
if (!gallery) return;
gallery.innerHTML = '';
images.forEach(img => {
const imgEl = document.createElement('div');
imgEl.className = 'gallery__image-placeholder';
imgEl.setAttribute('aria-label', `Bild aufgenommen um ${new Date(img.timestamp).toLocaleString()}`);
imgEl.textContent = `[Bild: ${img.filename}]`;
gallery.appendChild(imgEl);
});
const count = document.createElement('p');
count.className = 'gallery__count';
count.textContent = `Anzahl Bilder: ${images.length}`;
gallery.appendChild(count);
}
/**
* Render a quick summary of available data.
* @param {Array} temps
* @param {Array} images
*/
function renderSummary(temps, images) {
const summary = document.querySelector('#data-summary');
if (!summary) return;
const avgTemp = temps.reduce((acc, t) => acc + t.value, 0) / (temps.length || 1);
summary.textContent = `Durchschnittstemperatur: ${avgTemp.toFixed(1)}°C | Bilder: ${images.length}`;
}
/**
* Shows an error message in a container.
* @param {HTMLElement} container
* @param {string} msg
*/
function showError(container, msg) {
container.innerHTML = '';
const p = document.createElement('p');
p.className = 'error';
p.textContent = msg;
container.appendChild(p);
}
})();