Files
skyview.astronomiemuseum.de/public/geocron/earth-core.js
T
2026-04-02 15:40:06 +02:00

216 lines
8.3 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.
/**
* earth-core.js – Kern-Framework für die Erd-Visualisierung
* =============================================================
* Stellt Hilfsfunktionen, astronomische Berechnungen und ein
* Event-System bereit, auf das alle Plugins lauschen.
*
* Gefeuerte Events (auf window):
* earth:resize → { detail: { w, h } }
* earth:slow → { detail: { sun, w, h, now } } (~1×/min)
* earth:fast → { detail: { sun, w, h, now } } (~30 fps)
*
* Globales Objekt: window.EarthCore
*/
window.EarthCore = (function () {
// ═══════════════════════════════════════════════════════════════
// KONFIGURATION
// ═══════════════════════════════════════════════════════════════
const CFG = {
// ── Bildpfade ──────────────────────────────────────────────
IMG_DAY_DIR: "../images/planets/earth/day/",
IMG_EXT: ".jpg",
// ── Aktualisierungsintervalle ──────────────────────────────
UPDATE_FAST_MS: 1000, // ~30 fps
UPDATE_SLOW_MS: 60_000, // 1× pro Minute
};
// ═══════════════════════════════════════════════════════════════
// HILFSFUNKTIONEN (öffentlich über EarthCore)
// ═══════════════════════════════════════════════════════════════
const toRad = d => d * Math.PI / 180;
const toDeg = r => r * 180 / Math.PI;
const toX = (lon, w) => ((lon + 180) / 360) * w;
const toY = (lat, h) => ((90 - lat) / 180) * h;
/** Maximales 2:1-Rechteck im Earth-Container */
function calcSize() {
const wrapper = document.getElementById("earth-wrapper");
const W = wrapper?.clientWidth || window.innerWidth;
const H = wrapper?.clientHeight || window.innerHeight;
return W / H >= 2
? { w: H * 2, h: H }
: { w: W, h: W / 2 };
}
/**
* Erstellt einen Plugin-Canvas und hängt ihn in #earth-wrapper.
* @param {string} id – canvas.id
* @param {number} zIndex – CSS z-index
* @param {boolean} [noPointer] – pointerEvents: none (Standard: true)
*/
function createCanvas(id, zIndex, noPointer = true) {
const viewport = document.getElementById("earth-viewport");
if (!viewport) throw new Error("earth-core: #earth-viewport nicht gefunden");
const c = document.createElement("canvas");
c.id = id;
Object.assign(c.style, {
position: "absolute",
top: "0",
left: "0",
display: "block",
zIndex: String(zIndex),
pointerEvents: noPointer ? "none" : "auto",
});
viewport.appendChild(c);
return c;
}
/**
* Synchronisiert Größe eines Canvas auf aktuelle Erdgröße.
* Gibt true zurück wenn Größe gesetzt werden konnte.
*/
function syncCanvas(canvas, w, h) {
if (!w || !h) return false;
const rw = Math.round(w), rh = Math.round(h);
canvas.style.width = rw + "px";
canvas.style.height = rh + "px";
canvas.width = rw;
canvas.height = rh;
return true;
}
/**
* Zeichnet eine abgerundete Rechteck-Path (kein fill/stroke).
* Universell für alle Plugins nutzbar.
*/
function roundRectPath(ctx, x, y, w, h, r) {
const rr = Math.min(r, w / 2, h / 2);
ctx.beginPath();
ctx.moveTo(x + rr, y);
ctx.arcTo(x + w, y, x + w, y + h, rr);
ctx.arcTo(x + w, y + h, x, y + h, rr);
ctx.arcTo(x, y + h, x, y, rr);
ctx.arcTo(x, y, x + w, y, rr);
ctx.closePath();
}
// ═══════════════════════════════════════════════════════════════
// ASTRONOMISCHE BERECHNUNGEN
// ═══════════════════════════════════════════════════════════════
/** Subsolar-Punkt: wo steht die Sonne senkrecht? (Astronomy Engine) */
function subsolarPoint(date) {
try {
const vec = Astronomy.GeoVector(Astronomy.Body.Sun, date, true);
const dist = Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
const dec = toDeg(Math.asin(vec.z / dist));
const ra = toDeg(Math.atan2(vec.y, vec.x));
const gmst = Astronomy.SiderealTime(date) * 15; // Stunden → Grad
const lon = ((ra - gmst + 180) % 360 + 360) % 360 - 180;
return { lat: dec, lon: lon };
} catch (e) {
return { lat: 0, lon: 0 };
}
}
// ═══════════════════════════════════════════════════════════════
// ZUSTAND & EVENT-LOOP
// ═══════════════════════════════════════════════════════════════
let _sun = { lat: 0, lon: 0 };
let _size = { w: 0, h: 0 };
let _resizeRaf = 0;
let _resizeObserver = null;
function _buildDetail(now) {
return { sun: _sun, w: _size.w, h: _size.h, now };
}
function _fireSlow() {
const now = new Date();
_sun = subsolarPoint(now);
window.dispatchEvent(new CustomEvent("earth:slow", { detail: _buildDetail(now) }));
}
function _fireFast() {
window.dispatchEvent(new CustomEvent("earth:fast", { detail: _buildDetail(new Date()) }));
}
function _onResize() {
_size = calcSize();
const viewport = document.getElementById("earth-viewport");
if (viewport) {
viewport.style.width = Math.round(_size.w) + "px";
viewport.style.height = Math.round(_size.h) + "px";
}
window.dispatchEvent(new CustomEvent("earth:resize", { detail: { w: _size.w, h: _size.h } }));
_fireSlow();
_fireFast();
}
function _queueResize() {
if (_resizeRaf) cancelAnimationFrame(_resizeRaf);
_resizeRaf = requestAnimationFrame(() => {
_resizeRaf = 0;
_onResize();
});
}
window.addEventListener("resize", _queueResize);
// ── Start ──────────────────────────────────────────────────────
function _start() {
// Tagbild laden
const img = document.getElementById("earthDay");
if (img) {
const month = new Date().getMonth() + 1;
img.src = `${CFG.IMG_DAY_DIR}${month}${CFG.IMG_EXT}`;
}
_size = calcSize();
const viewport = document.getElementById("earth-viewport");
if (viewport) {
viewport.style.width = Math.round(_size.w) + "px";
viewport.style.height = Math.round(_size.h) + "px";
}
const wrapper = document.getElementById("earth-wrapper");
if (wrapper && typeof ResizeObserver !== "undefined") {
_resizeObserver = new ResizeObserver(() => _queueResize());
_resizeObserver.observe(wrapper);
}
_fireSlow();
_fireFast();
setInterval(_fireFast, CFG.UPDATE_FAST_MS);
setInterval(_fireSlow, CFG.UPDATE_SLOW_MS);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", _start);
} else {
_start();
}
// ═══════════════════════════════════════════════════════════════
// ÖFFENTLICHE API
// ═══════════════════════════════════════════════════════════════
return {
// Koordinaten-Helfer
toRad, toDeg, toX, toY,
// Canvas-Helfer
createCanvas, syncCanvas, roundRectPath,
// Astronomie
subsolarPoint,
// Größe
calcSize,
// Live-Getter
get sun() { return _sun; },
get size() { return _size; },
};
}());