Merge branch 'main' of https://github.com/Eskimue/skyview.astronomiemuseum.de
This commit is contained in:
+15
-15
@@ -85,7 +85,7 @@ function sendActivationNotification(string $username, string $email, ?string $di
|
||||
'',
|
||||
'Benutzername: ' . $username,
|
||||
'',
|
||||
'Viele Gruesse',
|
||||
'Viele Grüße',
|
||||
'Astronomiemuseum der Sternwarte Sonneberg',
|
||||
];
|
||||
|
||||
@@ -180,7 +180,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if ($targetUser && $targetUserId === $currentUserId && in_array($action, ['deactivate_user', 'change_role', 'delete_user'], true)) {
|
||||
$errors[] = 'Das eigene Administrationskonto kann hier nicht deaktiviert, geloescht oder umgestellt werden.';
|
||||
$errors[] = 'Das eigene Administrationskonto kann hier nicht deaktiviert, gelöscht oder umgestellt werden.';
|
||||
}
|
||||
|
||||
$currentUserIsMaster = isMasterRole((string) ($currentUser['role'] ?? ''));
|
||||
@@ -206,7 +206,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
!$currentUserIsMaster &&
|
||||
$targetUserIsMaster
|
||||
) {
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder ändern.';
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -214,7 +214,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$requestedRole === 'master' &&
|
||||
$masterCount > 0
|
||||
) {
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
|
||||
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder ändern.';
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -242,7 +242,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
(int)$targetUser['is_active'] === 1 &&
|
||||
$activeAdminCount <= 1
|
||||
) {
|
||||
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht geloescht werden.';
|
||||
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht gelöscht werden.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmtDelete->execute([
|
||||
':id' => $targetUserId,
|
||||
]);
|
||||
$_SESSION['admin_users_flash_success'] = 'Benutzerkonto wurde geloescht. Zugehoerige Standorte und Satelliten wurden automatisch entfernt.';
|
||||
$_SESSION['admin_users_flash_success'] = 'Benutzerkonto wurde gelöscht. Zugehörige Standorte und Satelliten wurden automatisch entfernt.';
|
||||
redirectToAdminUsers();
|
||||
}
|
||||
|
||||
@@ -442,8 +442,8 @@ if ($currentUserIsMaster) {
|
||||
|
||||
<div class="admin-users-layout">
|
||||
<section class="card admin-users-summary-card">
|
||||
<h2>Uebersicht</h2>
|
||||
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, loeschen und zwischen <strong>user</strong>, <strong>admin</strong> und <strong>master</strong> wechseln.</p>
|
||||
<h2>Übersicht</h2>
|
||||
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, löschen und zwischen <strong>user</strong>, <strong>admin</strong> und <strong>master</strong> wechseln.</p>
|
||||
|
||||
<?php if ($successMessage !== ''): ?>
|
||||
<p class="msg-success"><?= h($successMessage) ?></p>
|
||||
@@ -558,7 +558,7 @@ if ($currentUserIsMaster) {
|
||||
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['admin_users_csrf']) ?>">
|
||||
<input type="hidden" name="action" value="delete_user">
|
||||
<input type="hidden" name="user_id" value="<?= (int)$userRow['id'] ?>">
|
||||
<button type="submit" class="btn btn-danger btn-sm"<?= $isSelf ? ' disabled' : '' ?>>Benutzer loeschen</button>
|
||||
<button type="submit" class="btn btn-danger btn-sm"<?= $isSelf ? ' disabled' : '' ?>>Benutzer löschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
@@ -572,7 +572,7 @@ if ($currentUserIsMaster) {
|
||||
<?php if ($currentUserIsMaster): ?>
|
||||
<section class="card admin-users-table-card">
|
||||
<h2>Seitenfreigaben</h2>
|
||||
<p class="hint">Nur Master-Benutzer duerfen hier festlegen, welche Seiten normale Nutzer im Menue sehen und direkt aufrufen duerfen.</p>
|
||||
<p class="hint">Nur Master-Benutzer dürfen hier festlegen, welche Seiten normale Nutzer im Menü sehen und direkt aufrufen dürfen.</p>
|
||||
<div class="admin-users-table-wrap">
|
||||
<table class="data-table admin-users-table">
|
||||
<thead>
|
||||
@@ -616,7 +616,7 @@ if ($currentUserIsMaster) {
|
||||
<?php endforeach; ?>
|
||||
<td>
|
||||
<?php if ($isPrivilegedUser): ?>
|
||||
<span class="hint">Vollzugriff ueber Rolle</span>
|
||||
<span class="hint">Vollzugriff über Rolle</span>
|
||||
<?php else: ?>
|
||||
<button type="submit" class="btn btn-secondary btn-sm" form="<?= h($permissionFormId) ?>">Freigaben speichern</button>
|
||||
<?php endif; ?>
|
||||
@@ -643,11 +643,11 @@ if ($currentUserIsMaster) {
|
||||
|
||||
<dialog class="admin-dialog" id="deleteUserDialog">
|
||||
<form method="dialog" class="admin-dialog-card">
|
||||
<h3>Benutzer loeschen?</h3>
|
||||
<p id="deleteUserDialogText">Soll dieser Benutzer wirklich geloescht werden?</p>
|
||||
<h3>Benutzer löschen?</h3>
|
||||
<p id="deleteUserDialogText">Soll dieser Benutzer wirklich gelöscht werden?</p>
|
||||
<div class="admin-dialog-actions">
|
||||
<button type="button" class="btn btn-secondary" data-delete-choice="no">Abbrechen</button>
|
||||
<button type="button" class="btn btn-danger" data-delete-choice="yes">Loeschen</button>
|
||||
<button type="button" class="btn btn-danger" data-delete-choice="yes">Löschen</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
@@ -713,7 +713,7 @@ if ($currentUserIsMaster) {
|
||||
pendingDeleteForm = form;
|
||||
|
||||
const username = form.getAttribute('data-username') || 'dieser Benutzer';
|
||||
deleteDialogText.textContent = 'Benutzer "' + username + '" wirklich loeschen? Standorte und Satelliten dieses Kontos werden ebenfalls entfernt.';
|
||||
deleteDialogText.textContent = 'Benutzer "' + username + '" wirklich löschen? Standorte und Satelliten dieses Kontos werden ebenfalls entfernt.';
|
||||
deleteDialog.showModal();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,12 +63,12 @@ function manageablePublicPages(): array
|
||||
'jupitersystem.php' => 'Jupitersystem',
|
||||
'tagbogen.php' => 'Tagbogen',
|
||||
'sternenhimmel.php' => 'Sternenhimmel',
|
||||
'meteorshowers.php' => 'Meteorstroeme',
|
||||
'meteorshowers.php' => 'Meteorströme',
|
||||
'geocron/index.php' => 'Geocron',
|
||||
'geocron3d.php' => 'Geocron 3D',
|
||||
'mondfinsternis.php' => 'Mondfinsternis',
|
||||
'sonnenfinsternis.php' => 'Sonnenfinsternis',
|
||||
'bedeckung.php' => 'Bedeckungsphaenomene',
|
||||
'bedeckung.php' => 'Bedeckungsphänomene',
|
||||
'sternbedeckungen.php' => 'Mond-Stern-Bedeckungen',
|
||||
'monatsvorhersage.php' => 'Monatsvorhersage',
|
||||
'wochenvorhersage.php' => 'Wochenvorhersage',
|
||||
|
||||
+43
-43
@@ -53,15 +53,15 @@ $bodyClass = 'occ-page';
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="ic-name" id="exoName">HD 209458 b</div>
|
||||
<div class="ic-tag" id="exoTag">HEISSER JUPITER · 1999</div>
|
||||
<div class="ic-tag" id="exoTag">HEISSER JUPITER · 1999</div>
|
||||
<div class="ic-desc" id="exoDesc">Erster Exoplanet mit direkt nachgewiesenem Transit.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ctrl-block">
|
||||
<div class="ctrl-label">Orbitparameter</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vIncl">86.6°</span></div>
|
||||
<input type="range" class="sl" id="sIncl" data-tip="Inklination" data-tip-text="Neigung der Planetenbahn zur Sichtlinie. Bei 90° liegt die Bahn exakt vor dem Stern (maximaler Transit). Je kleiner, desto kürzer oder gar kein Transit." min="45" max="90" step="0.1" value="86.6">
|
||||
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vIncl">86.6°</span></div>
|
||||
<input type="range" class="sl" id="sIncl" data-tip="Inklination" data-tip-text="Neigung der Planetenbahn zur Sichtlinie. Bei 90° liegt die Bahn exakt vor dem Stern (maximaler Transit). Je kleiner, desto kürzer oder gar kein Transit." min="45" max="90" step="0.1" value="86.6">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">ABSTAND (a/R*)</span><span class="param-val" id="vDist">8.8</span></div>
|
||||
@@ -76,7 +76,7 @@ $bodyClass = 'occ-page';
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">ALBEDO</span><span class="param-val" id="vAlb">0.04</span></div>
|
||||
<input type="range" class="sl" id="sAlb" data-tip="Albedo" data-tip-text="Rückstrahlvermögen des Planeten (0 = schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt einen kleinen Helligkeitsbuckel vor dem Transit (Phasenkurve)." min="0" max="1" step="0.01" value="0.04">
|
||||
<input type="range" class="sl" id="sAlb" data-tip="Albedo" data-tip-text="Rückstrahlvermögen des Planeten (0 = schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt einen kleinen Helligkeitsbuckel vor dem Transit (Phasenkurve)." min="0" max="1" step="0.01" value="0.04">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@ $bodyClass = 'occ-page';
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="ic-name" id="multiName">TRAPPIST-1</div>
|
||||
<div class="ic-tag" id="multiTag">ULTRAKUEHLER M-ZWERG · 7 PLANETEN</div>
|
||||
<div class="ic-tag" id="multiTag">ULTRAKÜHLER M-ZWERG · 7 PLANETEN</div>
|
||||
<div class="ic-desc" id="multiDesc">Das bekannteste Mehrplanetensystem.</div>
|
||||
</div>
|
||||
<div class="planet-legend" id="planetLegend"></div>
|
||||
@@ -103,7 +103,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="ctrl-label">Zeitfenster</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">ZEITFENSTER (TAGE)</span><span class="param-val" id="vTimeWin">25</span></div>
|
||||
<input type="range" class="sl" id="sTimeWin" data-tip="Zeitfenster" data-tip-text="Dargestellter Zeitraum der Lichtkurve in Tagen. Längere Fenster zeigen mehr Transitevents mehrerer Planeten." min="5" max="200" step="1" value="25">
|
||||
<input type="range" class="sl" id="sTimeWin" data-tip="Zeitfenster" data-tip-text="Dargestellter Zeitraum der Lichtkurve in Tagen. Längere Fenster zeigen mehr Transitevents mehrerer Planeten." min="5" max="200" step="1" value="25">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,23 +114,23 @@ $bodyClass = 'occ-page';
|
||||
<div class="ctrl-label">Doppelsternsystem</div>
|
||||
<div class="sel-wrap">
|
||||
<select class="sel" id="binSelect">
|
||||
<option value="algol">Algol (β Persei)</option>
|
||||
<option value="algol">Algol (β Persei)</option>
|
||||
<option value="wuma">W Ursae Majoris</option>
|
||||
<option value="betalyr">Beta Lyrae (β Lyr)</option>
|
||||
<option value="betalyr">Beta Lyrae (β Lyr)</option>
|
||||
<option value="custom_bin">Benutzerdefiniert</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="ic-name" id="binName">Algol</div>
|
||||
<div class="ic-tag" id="binTag">β PERSEI · P=2.87 TAGE</div>
|
||||
<div class="ic-tag" id="binTag">β PERSEI · P=2.87 TAGE</div>
|
||||
<div class="ic-desc" id="binDesc">Der "Daemonenstern" - bekanntester Bedeckungsveraenderlicher.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ctrl-block">
|
||||
<div class="ctrl-label">Systemparameter</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vBinIncl">81.0°</span></div>
|
||||
<input type="range" class="sl" id="sBinIncl" data-tip="Inklination" data-tip-text="Neigung der Doppelsternbahn. Bei 90° bedeckt jeder Stern den anderen vollständig (flaches Minimum). Je kleiner, desto flacher die Bedeckung." min="45" max="90" step="0.1" value="81.0">
|
||||
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vBinIncl">81.0°</span></div>
|
||||
<input type="range" class="sl" id="sBinIncl" data-tip="Inklination" data-tip-text="Neigung der Doppelsternbahn. Bei 90° bedeckt jeder Stern den anderen vollständig (flaches Minimum). Je kleiner, desto flacher die Bedeckung." min="45" max="90" step="0.1" value="81.0">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<div class="param-head"><span class="param-name">ABSTAND (a/R1)</span><span class="param-val" id="vBinDist">5.3</span></div>
|
||||
@@ -152,7 +152,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="ctrl-label">Kenngroessen</div>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item" data-tip="Primaeres Minimum" data-tip-text="Tiefster Helligkeitsabfall in Prozent. Beim Planeten: (Rp/R*)^2. Beim Doppelstern: Bedeckung des helleren Sterns."><div class="stat-lbl">Prim. Min.</div><div class="stat-val" id="sDepth">--</div></div>
|
||||
<div class="stat-item" data-tip="Aktueller Flux" data-tip-text="Normierte Helligkeit des Systems zum aktuellen Zeitpunkt (1.0 = volle Helligkeit). Leuchtet grün, wenn gerade ein Transit oder eine Bedeckung stattfindet."><div class="stat-lbl">Flux jetzt</div><div class="stat-val" id="sFlux">1.000</div></div>
|
||||
<div class="stat-item" data-tip="Aktueller Flux" data-tip-text="Normierte Helligkeit des Systems zum aktuellen Zeitpunkt (1.0 = volle Helligkeit). Leuchtet grün, wenn gerade ein Transit oder eine Bedeckung stattfindet."><div class="stat-lbl">Flux jetzt</div><div class="stat-val" id="sFlux">1.000</div></div>
|
||||
<div class="stat-item" id="stat-item-3" data-tip="Impact-Parameter b" data-tip-text="Einzelplanet: Minimaler Abstand Planetenmitte-Sternmitte in Sternradien. b=0 -> zentraler Transit, b<1 -> Transit moeglich, b>1 -> kein Transit. Doppelstern: Sekundaeres Minimum. Mehrplaneten: Anzahl der Planeten."><div class="stat-lbl" id="statLbl3">Impact b</div><div class="stat-val" id="sStat3">--</div></div>
|
||||
<div class="stat-item" id="stat-item-4" data-tip="Weitere Kenngroesse" data-tip-text="Einzelplanet: Ob ein Transit geometrisch moeglich ist (b < 1+Rp/R*). Doppelstern: Umlaufzeit in Tagen. Mehrplaneten: Kuerzeste Periode im System."><div class="stat-lbl" id="statLbl4">Transit</div><div class="stat-val" id="sStat4">--</div></div>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="modal-head">
|
||||
<div>
|
||||
<div class="modal-head-title">HILFE & PARAMETER</div>
|
||||
<div class="modal-head-sub">Bedeckungsphänomene · Astronomiemuseum der Sternwarte Sonneberg</div>
|
||||
<div class="modal-head-sub">Bedeckungsphänomene · Astronomiemuseum der Sternwarte Sonneberg</div>
|
||||
</div>
|
||||
<button class="modal-close" id="helpClose">×</button>
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="help-section">
|
||||
<div class="help-section-title">Planetenparameter</div>
|
||||
<div class="help-row"><div class="help-key">Radius Rp/R*</div><div class="help-val">Planetenradius relativ zum Sternradius. Die Transit-Tiefe ist direkt proportional zum Flaechenverhaeltnis: Delta F ~= (Rp/R*)^2. Ein Planet mit 10% Sternradius erzeugt 1% Helligkeitsabfall.</div></div>
|
||||
<div class="help-row"><div class="help-key">Albedo</div><div class="help-val">Rückstrahlvermögen (0 = absolut schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt vor dem Transit einen schwachen Helligkeitsbuckel (reflektierte Phasenkurve) und während der Sekundärbedeckung ein Mini-Minimum.</div></div>
|
||||
<div class="help-row"><div class="help-key">Albedo</div><div class="help-val">Rückstrahlvermögen (0 = absolut schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt vor dem Transit einen schwachen Helligkeitsbuckel (reflektierte Phasenkurve) und während der Sekundärbedeckung ein Mini-Minimum.</div></div>
|
||||
</div>
|
||||
<div class="help-section">
|
||||
<div class="help-section-title">Kenngroessen</div>
|
||||
@@ -243,7 +243,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="help-tab-content" id="htab-multi">
|
||||
<div class="help-section">
|
||||
<div class="help-section-title">Mehrplanetensysteme</div>
|
||||
<div class="help-row"><div class="help-key">TRAPPIST-1</div><div class="help-val">Sieben felsige Planeten um einen ultrakühlen M-Zwergstern, 39 Lichtjahre entfernt. Alle wurden durch Transits entdeckt. Planeten e, f und g liegen in der habitablen Zone.</div></div>
|
||||
<div class="help-row"><div class="help-key">TRAPPIST-1</div><div class="help-val">Sieben felsige Planeten um einen ultrakühlen M-Zwergstern, 39 Lichtjahre entfernt. Alle wurden durch Transits entdeckt. Planeten e, f und g liegen in der habitablen Zone.</div></div>
|
||||
<div class="help-row"><div class="help-key">Kepler-11</div><div class="help-val">Sechs Planeten in sehr engen Orbits - alle dichter am Stern als die Venus. 2010 als erstes bekanntes Sechs-Planeten-System entdeckt.</div></div>
|
||||
<div class="help-row"><div class="help-key">Sonnensystem</div><div class="help-val">Wie wuerde unser System von aussen aussehen? Alle inneren Planeten auf maximale Transitinklination (90 Grad). Man sieht: Merkur transitet haeufig, Mars sehr selten.</div></div>
|
||||
</div>
|
||||
@@ -265,7 +265,7 @@ $bodyClass = 'occ-page';
|
||||
<div class="help-section-title">Parameter</div>
|
||||
<div class="help-row"><div class="help-key">R2/R1</div><div class="help-val">Radius des Sekundaersterns relativ zum Primaerstern. Beeinflusst die Tiefe beider Minima und die Transitdauer.</div></div>
|
||||
<div class="help-row"><div class="help-key">L2/L1</div><div class="help-val">Flaechenhelligkeit des Sekundaersterns. Bei 1.0 gleich hell -> gleich tiefe Minima. Bei kleinen Werten (z.B. Algol: 0.017) ist das sekundaere Minimum kaum sichtbar.</div></div>
|
||||
<div class="help-row"><div class="help-key">Prim. / Sek. Min.</div><div class="help-val">Tiefe des primären (Sekundärstern bedeckt Primärstern) und sekundären Minimums (umgekehrt) in Prozent des Gesamtlichts.</div></div>
|
||||
<div class="help-row"><div class="help-key">Prim. / Sek. Min.</div><div class="help-val">Tiefe des primären (Sekundärstern bedeckt Primärstern) und sekundären Minimums (umgekehrt) in Prozent des Gesamtlichts.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -274,13 +274,13 @@ $bodyClass = 'occ-page';
|
||||
<div class="help-section">
|
||||
<div class="help-section-title">Lichtkurve</div>
|
||||
<div class="help-row"><div class="help-key">X-Achse</div><div class="help-val">Orbitphase (0-360 Grad) beim Einzel- und Doppelstern-Modus. Zeitachse in Tagen im Mehrplaneten-Modus.</div></div>
|
||||
<div class="help-row"><div class="help-key">Y-Achse</div><div class="help-val">Normierter Flux (1.0 = volle Helligkeit). Werte unter 1.0 zeigen Bedeckungen, Werte über 1.0 reflektiertes Planetenlicht.</div></div>
|
||||
<div class="help-row"><div class="help-key">Y-Achse</div><div class="help-val">Normierter Flux (1.0 = volle Helligkeit). Werte unter 1.0 zeigen Bedeckungen, Werte über 1.0 reflektiertes Planetenlicht.</div></div>
|
||||
<div class="help-row"><div class="help-key">Weisser Punkt</div><div class="help-val">Aktuelle Position in der Lichtkurve. Durch Klicken/Ziehen in der Lichtkurve kann die Phase direkt eingestellt werden.</div></div>
|
||||
<div class="help-row"><div class="help-key">Gestrichelte Linie</div><div class="help-val">Referenzlinie bei Flux = 1.0 (unbedeckter Stern).</div></div>
|
||||
</div>
|
||||
<div class="help-section">
|
||||
<div class="help-section-title">Visualisierung</div>
|
||||
<div class="help-row"><div class="help-key">3D-Ansicht</div><div class="help-val">Zeigt das System aus Beobachterperspektive. Die gestrichelte Ellipse ist die projizierte Bahn. Der grüne Badge leuchtet auf, wenn ein Transit/eine Bedeckung aktiv ist.</div></div>
|
||||
<div class="help-row"><div class="help-key">3D-Ansicht</div><div class="help-val">Zeigt das System aus Beobachterperspektive. Die gestrichelte Ellipse ist die projizierte Bahn. Der grüne Badge leuchtet auf, wenn ein Transit/eine Bedeckung aktiv ist.</div></div>
|
||||
<div class="help-row"><div class="help-key">Geschwindigkeit</div><div class="help-val">0.5x bis 5x beschleunigt oder verlangsamt die Animation. Nuetzlich um schnelle Transitevents zu beobachten oder ruhig zu studieren.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -297,21 +297,21 @@ const PC=['#70e0a0','#6090e0','#e09040','#e06070','#a070e0','#40c0e0','#e0c040']
|
||||
|
||||
const DB_S={
|
||||
custom: {name:'Benutzerdefiniert',tag:'FREIE PARAMETER',desc:'Stelle alle Parameter manuell ein.',incl:85,dist:8,rr:.12,alb:.1},
|
||||
hd209458b: {name:'HD 209458 b',tag:'HEISSER JUPITER · 1999 · "OSIRIS"',desc:'Erster Exoplanet mit direkt nachgewiesenem Transit. Wegen seiner expandierenden Atmosphäre auch "Osiris" genannt.',incl:86.6,dist:8.76,rr:.1209,alb:.04},
|
||||
wasp12b: {name:'WASP-12 b',tag:'HEISSER JUPITER · 2008',desc:'Umkreist seinen Stern in nur 1,09 Tagen - und wird dabei langsam durch Gezeitenkraefte zerrissen.',incl:83.4,dist:3.09,rr:.1175,alb:.06},
|
||||
hatp32b: {name:'HAT-P-32 b',tag:'HEISSER JUPITER · 2011',desc:'Stark aufgeblähter Riesenplanet mit einer der tiefsten bekannten Transit-Lichtkurven.',incl:88.9,dist:4.12,rr:.150,alb:.08},
|
||||
kepler7b: {name:'Kepler-7 b',tag:'HEISSER JUPITER · KEPLER · 2010',desc:'Rekord-Albedo von 0.35 - hoechste bekannter Exoplaneten. Helle Wolkenbaender reflektieren das Sternlicht.',incl:85.7,dist:7.32,rr:.0880,alb:.35},
|
||||
wasp96b: {name:'WASP-96 b',tag:'GASRIESE · JWST-ERSTES LICHT 2022',desc:'Erster Exoplanet dessen Atmosphäre vom James Webb Space Telescope vollständig spektroskopiert wurde.',incl:85.1,dist:9.33,rr:.1148,alb:.10},
|
||||
'55cnce': {name:'55 Cancri e',tag:'SUPER-ERDE · 2004 · LAVA-WELT',desc:'Eine Welt mit möglicherweise geschmolzener Oberfläche und Lava-Ozeanen. Umkreist seinen Stern in nur 17.7 Stunden.',incl:83.0,dist:3.52,rr:.0182,alb:.30},
|
||||
trappist1b:{name:'TRAPPIST-1 b',tag:'FELSIGER PLANET · 2016',desc:'Innerster der sieben Planeten des TRAPPIST-1-Systems. Fuer Leben zu heiss, aber wichtiger Vergleichsplanet.',incl:89.6,dist:20.9,rr:.0834,alb:.10},
|
||||
hd209458b: {name:'HD 209458 b',tag:'HEISSER JUPITER · 1999 · "OSIRIS"',desc:'Erster Exoplanet mit direkt nachgewiesenem Transit. Wegen seiner expandierenden Atmosphäre auch "Osiris" genannt.',incl:86.6,dist:8.76,rr:.1209,alb:.04},
|
||||
wasp12b: {name:'WASP-12 b',tag:'HEISSER JUPITER · 2008',desc:'Umkreist seinen Stern in nur 1,09 Tagen - und wird dabei langsam durch Gezeitenkräfte zerrissen.',incl:83.4,dist:3.09,rr:.1175,alb:.06},
|
||||
hatp32b: {name:'HAT-P-32 b',tag:'HEISSER JUPITER · 2011',desc:'Stark aufgeblähter Riesenplanet mit einer der tiefsten bekannten Transit-Lichtkurven.',incl:88.9,dist:4.12,rr:.150,alb:.08},
|
||||
kepler7b: {name:'Kepler-7 b',tag:'HEISSER JUPITER · KEPLER · 2010',desc:'Rekord-Albedo von 0.35 - höchste bekannter Exoplaneten. Helle Wolkenbänder reflektieren das Sternlicht.',incl:85.7,dist:7.32,rr:.0880,alb:.35},
|
||||
wasp96b: {name:'WASP-96 b',tag:'GASRIESE · JWST-ERSTES LICHT 2022',desc:'Erster Exoplanet dessen Atmosphäre vom James Webb Space Telescope vollständig spektroskopiert wurde.',incl:85.1,dist:9.33,rr:.1148,alb:.10},
|
||||
'55cnce': {name:'55 Cancri e',tag:'SUPER-ERDE · 2004 · LAVA-WELT',desc:'Eine Welt mit möglicherweise geschmolzener Oberfläche und Lava-Ozeanen. Umkreist seinen Stern in nur 17.7 Stunden.',incl:83.0,dist:3.52,rr:.0182,alb:.30},
|
||||
trappist1b:{name:'TRAPPIST-1 b',tag:'FELSIGER PLANET · 2016',desc:'Innerster der sieben Planeten des TRAPPIST-1-Systems. Für Leben zu heiß, aber wichtiger Vergleichsplanet.',incl:89.6,dist:20.9,rr:.0834,alb:.10},
|
||||
};
|
||||
|
||||
const DB_M={
|
||||
trappist1:{name:'TRAPPIST-1',tag:'ULTRAKUEHLER M-ZWERG · 7 PLANETEN · 2017',desc:'Das bekannteste Mehrplanetensystem mit sieben felsigen Welten, darunter drei in der habitablen Zone. Alle Planeten wurden durch Transits entdeckt.',timeWindow:25,
|
||||
trappist1:{name:'TRAPPIST-1',tag:'ULTRAKÜHLER M-ZWERG · 7 PLANETEN · 2017',desc:'Das bekannteste Mehrplanetensystem mit sieben felsigen Welten, darunter drei in der habitablen Zone. Alle Planeten wurden durch Transits entdeckt.',timeWindow:25,
|
||||
planets:[{name:'b',rr:.0834,dist:20.9,incl:89.56,period:1.511},{name:'c',rr:.0839,dist:28.5,incl:89.70,period:2.422},{name:'d',rr:.0617,dist:39.6,incl:89.80,period:4.050},{name:'e',rr:.0730,dist:51.5,incl:89.86,period:6.101},{name:'f',rr:.0780,dist:68.4,incl:89.68,period:9.207},{name:'g',rr:.0880,dist:83.2,incl:89.50,period:12.354},{name:'h',rr:.0550,dist:109,incl:89.76,period:18.767}]},
|
||||
kepler11:{name:'Kepler-11',tag:'G-ZWERG · 6 PLANETEN · KEPLER · 2010',desc:'Ein kompaktes Sechs-Planeten-System. Alle sechs Planeten wurden gleichzeitig von Kepler entdeckt - damals eine Sensation.',timeWindow:50,
|
||||
kepler11:{name:'Kepler-11',tag:'G-ZWERG · 6 PLANETEN · KEPLER · 2010',desc:'Ein kompaktes Sechs-Planeten-System. Alle sechs Planeten wurden gleichzeitig von Kepler entdeckt - damals eine Sensation.',timeWindow:50,
|
||||
planets:[{name:'b',rr:.0191,dist:13.3,incl:88.5,period:10.304},{name:'c',rr:.0262,dist:16.8,incl:88.6,period:13.024},{name:'d',rr:.0280,dist:22.6,incl:89.3,period:22.684},{name:'e',rr:.0360,dist:26.4,incl:88.8,period:31.996},{name:'f',rr:.0264,dist:33.1,incl:89.4,period:46.689},{name:'g',rr:.0358,dist:53.8,incl:89.0,period:118.38}]},
|
||||
solar:{name:'Sonnensystem',tag:'G2V-STERN · INNERE PLANETEN · TRANSIT-PERSPEKTIVE',desc:'Wie wuerde unser Sonnensystem von aussen als Transitsystem erscheinen? Alle inneren Planeten auf maximale Transit-Inklination.',timeWindow:120,
|
||||
solar:{name:'Sonnensystem',tag:'G2V-STERN · INNERE PLANETEN · TRANSIT-PERSPEKTIVE',desc:'Wie würde unser Sonnensystem von außen als Transitsystem erscheinen? Alle inneren Planeten auf maximale Transit-Inklination.',timeWindow:120,
|
||||
planets:[{name:'Merkur',rr:.00351,dist:83.2,incl:90.0,period:87.97},{name:'Venus',rr:.00870,dist:155.5,incl:90.0,period:224.7},{name:'Erde',rr:.00916,dist:215.1,incl:90.0,period:365.25},{name:'Mars',rr:.00487,dist:327.7,incl:90.0,period:686.97}]},
|
||||
};
|
||||
|
||||
@@ -403,14 +403,14 @@ function recompute(){
|
||||
document.getElementById('statLbl4').textContent='Transit';
|
||||
document.getElementById('stat-item-3').dataset.tip='Impact-Parameter b';
|
||||
document.getElementById('stat-item-3').dataset.tipText='Minimaler Abstand Planetenmitte-Sternmitte in Sternradien. b=0 -> zentraler Transit, b<1 -> Transit moeglich, b>1 -> kein Transit. Minimum 45 Grad.';
|
||||
document.getElementById('stat-item-4').dataset.tip='Transit möglich?';
|
||||
document.getElementById('stat-item-4').dataset.tip='Transit möglich?';
|
||||
document.getElementById('stat-item-4').dataset.tipText='Ob ein Transit geometrisch moeglich ist. Abhaengig vom Impact-Parameter b und dem Planetenradius: Transit wenn b < 1 + Rp/R*.';
|
||||
const el=document.getElementById('sStat4');
|
||||
const ok=b<1+S.rr;el.textContent=ok?'JA':'NEIN';el.style.color=ok?'#70e0a0':'#e07070';
|
||||
}else if(S.mode==='binary'){
|
||||
document.getElementById('statLbl3').textContent='Sek. Min.';
|
||||
document.getElementById('stat-item-3').dataset.tip='Sekundäres Minimum';
|
||||
document.getElementById('stat-item-3').dataset.tipText='Tiefe des zweiten Helligkeitseinbruchs, wenn der schwächere Stern hinter dem helleren verschwindet. Bei Algol kaum sichtbar (0.017), bei W UMa fast gleich tief wie das primäre Minimum.';
|
||||
document.getElementById('stat-item-3').dataset.tip='Sekundäres Minimum';
|
||||
document.getElementById('stat-item-3').dataset.tipText='Tiefe des zweiten Helligkeitseinbruchs, wenn der schwächere Stern hinter dem helleren verschwindet. Bei Algol kaum sichtbar (0.017), bei W UMa fast gleich tief wie das primäre Minimum.';
|
||||
document.getElementById('stat-item-4').dataset.tip='Umlaufzeit';
|
||||
document.getElementById('stat-item-4').dataset.tipText='Periode des Doppelsternsystems in Tagen. W UMa: 0.33 d (8 Stunden!), Algol: 2.87 d, Beta Lyrae: 12.94 d.';
|
||||
// find secondary minimum: deepest point at least N/4 away from primary minimum
|
||||
@@ -431,9 +431,9 @@ function recompute(){
|
||||
const sys=DB_M[S.multiPreset];
|
||||
document.getElementById('statLbl3').textContent='Planeten';
|
||||
document.getElementById('stat-item-3').dataset.tip='Anzahl Planeten';
|
||||
document.getElementById('stat-item-3').dataset.tipText='Anzahl der Planeten im ausgewählten System. TRAPPIST-1: 7 Planeten, Kepler-11: 6 Planeten, Sonnensystem: 4 innere Planeten.';
|
||||
document.getElementById('stat-item-4').dataset.tip='Kürzeste Periode';
|
||||
document.getElementById('stat-item-4').dataset.tipText='Umlaufzeit des innersten Planeten in Tagen. Planeten mit kurzer Periode transiten häufiger und sind daher leichter zu entdecken.';
|
||||
document.getElementById('stat-item-3').dataset.tipText='Anzahl der Planeten im ausgewählten System. TRAPPIST-1: 7 Planeten, Kepler-11: 6 Planeten, Sonnensystem: 4 innere Planeten.';
|
||||
document.getElementById('stat-item-4').dataset.tip='Kürzeste Periode';
|
||||
document.getElementById('stat-item-4').dataset.tipText='Umlaufzeit des innersten Planeten in Tagen. Planeten mit kurzer Periode transiten häufiger und sind daher leichter zu entdecken.';
|
||||
document.getElementById('sStat3').textContent=sys.planets.length;
|
||||
document.getElementById('statLbl4').textContent='P-min (d)';
|
||||
document.getElementById('sStat4').textContent=sys.planets[0].period.toFixed(2);
|
||||
@@ -795,7 +795,7 @@ function drawLC(){
|
||||
if(S.mode==='multi'){
|
||||
const tw=S.timeWindow;[0,.17,.33,.5,.67,.83,1].forEach(f=>{lCx.fillText((f*tw).toFixed(0)+'d',ml+f*pw,mt+ph+18);});
|
||||
}else{
|
||||
[0,60,120,180,240,300,360].forEach(deg=>{lCx.fillText(deg+'°',ml+(deg/360)*pw,mt+ph+18);});
|
||||
[0,60,120,180,240,300,360].forEach(deg=>{lCx.fillText(deg+'°',ml+(deg/360)*pw,mt+ph+18);});
|
||||
}
|
||||
// y labels
|
||||
lCx.textAlign='right';
|
||||
@@ -832,14 +832,14 @@ function applySingle(key){
|
||||
updSL();recompute();
|
||||
}
|
||||
function updSL(){
|
||||
document.getElementById('vIncl').textContent=S.incl.toFixed(1)+'°';
|
||||
document.getElementById('vIncl').textContent=S.incl.toFixed(1)+'°';
|
||||
document.getElementById('vDist').textContent=S.dist.toFixed(1);
|
||||
document.getElementById('vRad').textContent=S.rr.toFixed(3);
|
||||
document.getElementById('vAlb').textContent=S.alb.toFixed(2);
|
||||
}
|
||||
function markCustS(){const p=DB_S.custom;document.getElementById('exoSelect').value='custom';document.getElementById('exoName').textContent=p.name;document.getElementById('exoTag').textContent=p.tag;document.getElementById('exoDesc').textContent=p.desc;}
|
||||
document.getElementById('exoSelect').addEventListener('change',e=>applySingle(e.target.value));
|
||||
[['sIncl','incl','vIncl',v=>v.toFixed(1)+'°'],['sDist','dist','vDist',v=>v.toFixed(1)],['sRad','rr','vRad',v=>v.toFixed(3)],['sAlb','alb','vAlb',v=>v.toFixed(2)]].forEach(([id,key,lbl,fmt])=>{
|
||||
[["sIncl","incl","vIncl",v=>v.toFixed(1)+"°"],['sDist','dist','vDist',v=>v.toFixed(1)],['sRad','rr','vRad',v=>v.toFixed(3)],['sAlb','alb','vAlb',v=>v.toFixed(2)]].forEach(([id,key,lbl,fmt])=>{
|
||||
document.getElementById(id).addEventListener('input',function(){S[key]=parseFloat(this.value);document.getElementById(lbl).textContent=fmt(S[key]);markCustS();recompute();});
|
||||
});
|
||||
|
||||
@@ -866,14 +866,14 @@ function applyBinary(key){
|
||||
updBL();recompute();
|
||||
}
|
||||
function updBL(){
|
||||
document.getElementById('vBinIncl').textContent=S.binIncl.toFixed(1)+'°';
|
||||
document.getElementById('vBinIncl').textContent=S.binIncl.toFixed(1)+'°';
|
||||
document.getElementById('vBinDist').textContent=S.binDist.toFixed(1);
|
||||
document.getElementById('vBinR2').textContent=S.binR2.toFixed(2);
|
||||
document.getElementById('vBinSB').textContent=S.binSB.toFixed(3);
|
||||
}
|
||||
function markCustB(){const b=DB_B.custom_bin;document.getElementById('binSelect').value='custom_bin';document.getElementById('binName').textContent=b.name;document.getElementById('binTag').textContent=b.tag;document.getElementById('binDesc').textContent=b.desc;}
|
||||
document.getElementById('binSelect').addEventListener('change',e=>applyBinary(e.target.value));
|
||||
[['sBinIncl','binIncl','vBinIncl',v=>v.toFixed(1)+'°'],['sBinDist','binDist','vBinDist',v=>v.toFixed(1)],['sBinR2','binR2','vBinR2',v=>v.toFixed(2)],['sBinSB','binSB','vBinSB',v=>v.toFixed(3)]].forEach(([id,key,lbl,fmt])=>{
|
||||
[["sBinIncl","binIncl","vBinIncl",v=>v.toFixed(1)+"°"],['sBinDist','binDist','vBinDist',v=>v.toFixed(1)],['sBinR2','binR2','vBinR2',v=>v.toFixed(2)],['sBinSB','binSB','vBinSB',v=>v.toFixed(3)]].forEach(([id,key,lbl,fmt])=>{
|
||||
document.getElementById(id).addEventListener('input',function(){S[key]=parseFloat(this.value);document.getElementById(lbl).textContent=fmt(S[key]);markCustB();recompute();});
|
||||
});
|
||||
|
||||
@@ -957,13 +957,13 @@ window.addEventListener('mousemove', e => {
|
||||
if (S.mode === 'binary') {
|
||||
S.binIncl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
|
||||
document.getElementById('sBinIncl').value = S.binIncl;
|
||||
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
|
||||
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
|
||||
markCustB(); recompute();
|
||||
} else {
|
||||
const minIncl = S.mode === 'single' ? 60 : 60;
|
||||
S.incl = Math.max(minIncl, Math.min(90, S.vizDragStartIncl + delta));
|
||||
document.getElementById('sIncl').value = S.incl;
|
||||
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
|
||||
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
|
||||
if (S.mode === 'single') { markCustS(); }
|
||||
recompute();
|
||||
}
|
||||
@@ -986,12 +986,12 @@ vCv.addEventListener('touchmove', e => {
|
||||
if (S.mode === 'binary') {
|
||||
S.binIncl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
|
||||
document.getElementById('sBinIncl').value = S.binIncl;
|
||||
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
|
||||
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
|
||||
markCustB(); recompute();
|
||||
} else {
|
||||
S.incl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
|
||||
document.getElementById('sIncl').value = S.incl;
|
||||
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
|
||||
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
|
||||
if (S.mode === 'single') markCustS();
|
||||
recompute();
|
||||
}
|
||||
|
||||
@@ -1301,6 +1301,12 @@ footer a:hover { color: var(--gold); }
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.favorites-loading-hint-error {
|
||||
border-color: rgba(200, 60, 60, 0.35);
|
||||
background: rgba(200, 60, 60, 0.12);
|
||||
color: #f08080;
|
||||
}
|
||||
|
||||
.favorites-loading-form.is-loading .btn {
|
||||
opacity: 0.75;
|
||||
cursor: wait;
|
||||
@@ -1439,6 +1445,8 @@ footer a:hover { color: var(--gold); }
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
margin: auto;
|
||||
max-width: min(92vw, 560px);
|
||||
}
|
||||
|
||||
.admin-dialog::backdrop {
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@
|
||||
require_once __DIR__ . '/auth_helpers.php';
|
||||
$title = isset($pageTitle) && is_string($pageTitle) && $pageTitle !== ''
|
||||
? $pageTitle
|
||||
: 'Skyview & AstroTools — Astronomiemuseum der Sternwarte Sonneberg';
|
||||
: 'Skyview & AstroTools - Astronomiemuseum der Sternwarte Sonneberg';
|
||||
|
||||
$bodyClassAttr = '';
|
||||
if (isset($bodyClass) && is_string($bodyClass) && trim($bodyClass) !== '') {
|
||||
@@ -83,7 +83,7 @@ if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCur
|
||||
<main class="container" style="padding-top: 4rem; padding-bottom: 4rem;">
|
||||
<section class="card">
|
||||
<h1>Zugriff verweigert</h1>
|
||||
<p>Diese Seite ist aktuell nur fuer Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
|
||||
<p>Diese Seite ist aktuell nur für Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
|
||||
<p><a class="btn btn-primary" href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>">Abmelden</a></p>
|
||||
</section>
|
||||
</main>
|
||||
@@ -125,7 +125,8 @@ $menuGroups = [
|
||||
['href' => 'jupitersystem.php', 'label' => 'Jupitersystem'],
|
||||
['href' => 'tagbogen.php', 'label' => 'Tagbogen'],
|
||||
['href' => 'sternenhimmel.php', 'label' => 'Sternenhimmel'],
|
||||
['href' => 'meteorshowers.php', 'label' => 'Meteorstroeme'],
|
||||
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
|
||||
['href' => 'meteorshowers.php', 'label' => 'Meteorströme'],
|
||||
['href' => 'geocron/index.php', 'label' => 'Geocron'],
|
||||
['href' => 'geocron3d.php', 'label' => 'Geocron 3D'],
|
||||
],
|
||||
@@ -149,7 +150,6 @@ $adminLinks = [
|
||||
$accountLinks = [
|
||||
['href' => 'settings.php', 'label' => 'Einstellungen'],
|
||||
['href' => 'my_favorites.php', 'label' => 'Favoriten'],
|
||||
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
|
||||
];
|
||||
|
||||
if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
|
||||
@@ -288,7 +288,7 @@ if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<div class="header-menu">
|
||||
<button type="button" class="menu-toggle" id="menuToggle" aria-expanded="false" aria-controls="headerMenuPanel" aria-label="Menue oeffnen">
|
||||
<button type="button" class="menu-toggle" id="menuToggle" aria-expanded="false" aria-controls="headerMenuPanel" aria-label="Menü öffnen">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
|
||||
@@ -88,7 +88,7 @@ function findMinimum(bodyName, startMs, endMs) {
|
||||
|
||||
function classifyVisibility(sunAltDeg) {
|
||||
if (sunAltDeg > -5) return { key: 'day', label: 'Tag' };
|
||||
if (sunAltDeg >= -10) return { key: 'twilight', label: 'Daemmerung' };
|
||||
if (sunAltDeg >= -10) return { key: 'twilight', label: 'Dämmerung' };
|
||||
return { key: 'night', label: 'Nacht' };
|
||||
}
|
||||
|
||||
|
||||
@@ -1145,9 +1145,8 @@ async function initJupiterSystem() {
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x03050c, 0.00015);
|
||||
|
||||
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
|
||||
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 2000);
|
||||
textureLoader = new THREE.TextureLoader();
|
||||
moonGroups = new Map();
|
||||
moonShadowCasters = new Map();
|
||||
@@ -1161,7 +1160,7 @@ async function initJupiterSystem() {
|
||||
cameraTarget = new THREE.Vector3(0, 0, 0);
|
||||
rotationEqjToEcl = AstronomyEngine.Rotation_EQJ_ECL();
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0x334466, 0.8);
|
||||
const ambientLight = new THREE.AmbientLight(0x334466, 0.2);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const hemiLight = new THREE.HemisphereLight(0x445577, 0x111a2e, 0.6);
|
||||
@@ -1180,6 +1179,8 @@ async function initJupiterSystem() {
|
||||
sunLight.shadow.camera.bottom = -40;
|
||||
sunLight.shadow.camera.updateProjectionMatrix();
|
||||
sunLight.shadow.radius = 0.5;
|
||||
sunLight.shadow.bias = 0.0008;
|
||||
sunLight.shadow.normalBias = 0.02;
|
||||
scene.add(sunLight);
|
||||
scene.add(sunLight.target); // Pflicht: sonst ignoriert Three.js die Shadow-Camera-Ausrichtung
|
||||
|
||||
|
||||
+15
-15
@@ -4,12 +4,12 @@ declare(strict_types=1);
|
||||
session_start();
|
||||
|
||||
$loggedIn = isset($_SESSION['user_id']);
|
||||
$pageTitle = 'Meteorstroeme - AstroTools';
|
||||
$pageTitle = 'Meteorströme - AstroTools';
|
||||
$bodyClass = 'admin-page meteorshowers-page';
|
||||
$publicBasePath = './';
|
||||
$headerIntroPre = 'Beobachtung';
|
||||
$headerIntroTitle = 'Meteorstroeme';
|
||||
$headerIntroSub = 'Liste der importierten Meteorstroeme aus der Datenbank mit Angaben in solarer Laenge.';
|
||||
$headerIntroTitle = 'Meteorströme';
|
||||
$headerIntroSub = 'Liste der importierten Meteorströme aus der Datenbank mit Angaben in solarer Länge.';
|
||||
|
||||
$config = require __DIR__ . '/../config/database.php';
|
||||
|
||||
@@ -314,10 +314,10 @@ require __DIR__ . '/header.php';
|
||||
|
||||
<?php
|
||||
$statsCards = [
|
||||
['label' => 'Importierte Meteorstroeme', 'value' => (string) count($allShowers)],
|
||||
['label' => 'Importierte Meteorströme', 'value' => (string) count($allShowers)],
|
||||
['label' => 'Davon aktuell aktiv', 'value' => (string) $activeCount],
|
||||
['label' => 'Hoechste ZHR', 'value' => $topZhr !== null ? (string) $topZhr : '-'],
|
||||
['label' => 'Aktuelle solare Laenge', 'value' => number_format($todaySolarLongitude, 2, ',', '.') . '°'],
|
||||
['label' => 'Höchste ZHR', 'value' => $topZhr !== null ? (string) $topZhr : '-'],
|
||||
['label' => 'Aktuelle solare Länge', 'value' => number_format($todaySolarLongitude, 2, ',', '.') . '°'],
|
||||
['label' => 'Datumsumrechnung', 'value' => (string) $displayYear],
|
||||
];
|
||||
?>
|
||||
@@ -343,29 +343,29 @@ $statsCards = [
|
||||
id="q"
|
||||
name="q"
|
||||
value="<?= h($searchQuery) ?>"
|
||||
placeholder="Code, Bezeichnung oder Mutterkoerper"
|
||||
placeholder="Code, Bezeichnung oder Mutterkörper"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mode">Anzeige</label>
|
||||
<select id="mode" name="mode">
|
||||
<option value="all" <?= $showMode === 'all' ? 'selected' : '' ?>>Alle Meteorstroeme</option>
|
||||
<option value="all" <?= $showMode === 'all' ? 'selected' : '' ?>>Alle Meteorströme</option>
|
||||
<option value="active" <?= $showMode === 'active' ? 'selected' : '' ?>>Nur aktuell aktive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<button type="submit" class="btn btn-primary">Anzeigen</button>
|
||||
<a href="meteorshowers.php" class="btn btn-secondary">Zuruecksetzen</a>
|
||||
<a href="meteorshowers.php" class="btn btn-secondary">Zurücksetzen</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Liste der Meteorstroeme</h2>
|
||||
<p class="hint">Die Gradangaben in Aktivitaet und Maximum sind Werte der solaren Laenge. Zusaetzlich wird dafuer ein Kalenderdatum im Jahr <?= h((string) $displayYear) ?> angezeigt.</p>
|
||||
<h2>Liste der Meteorströme</h2>
|
||||
<p class="hint">Die Gradangaben in Aktivität und Maximum sind Werte der solaren Länge. Zusätzlich wird dafür ein Kalenderdatum im Jahr <?= h((string) $displayYear) ?> angezeigt.</p>
|
||||
<?php if ($showers === []): ?>
|
||||
<p class="hint">Keine Meteorstroeme fuer diese Auswahl gefunden.</p>
|
||||
<p class="hint">Keine Meteorströme für diese Auswahl gefunden.</p>
|
||||
<?php else: ?>
|
||||
<div class="table-wrap">
|
||||
<table class="data-table">
|
||||
@@ -376,11 +376,11 @@ $statsCards = [
|
||||
<th>IAU</th>
|
||||
<th>Bezeichnung</th>
|
||||
<th>ZHR</th>
|
||||
<th>Aktivitaet (solare Laenge)</th>
|
||||
<th>Maximum (solare Laenge)</th>
|
||||
<th>Aktivität (solare Länge)</th>
|
||||
<th>Maximum (solare Länge)</th>
|
||||
<th>Radiant</th>
|
||||
<th>v<sub>g</sub></th>
|
||||
<th>Mutterkoerper</th>
|
||||
<th>Mutterkörper</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -38,7 +38,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
|
||||
|
||||
$decoded = json_decode($joined, true);
|
||||
if (!is_array($decoded)) {
|
||||
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gueltiges JSON.' : 'Python lieferte keine Ausgabe zurueck.';
|
||||
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gültiges JSON.' : 'Python lieferte keine Ausgabe zurück.';
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ if ($isAjax) {
|
||||
if ($utcIso === '') {
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => 'utc_iso wurde nicht uebergeben.',
|
||||
'error' => 'utc_iso wurde nicht übergeben.',
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
@@ -1071,7 +1071,7 @@ async function exportMoonPng() {
|
||||
const size = getMoonExportSize();
|
||||
const transparentBackground = useTransparentMoonExport();
|
||||
if (!size) {
|
||||
updateMoonExportStatus('Bitte eine gueltige Bildgroesse eingeben.', 'error');
|
||||
updateMoonExportStatus('Bitte eine gültige Bildgröße eingeben.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+281
-31
@@ -123,6 +123,53 @@ function countUserRows(PDO $pdo, string $tableName, int $userId): int
|
||||
return (int) $stmtCount->fetchColumn();
|
||||
}
|
||||
|
||||
function validateLocationForm(array $input, array &$errors): array
|
||||
{
|
||||
$name = trim((string) ($input['name'] ?? ''));
|
||||
$latitude = trim((string) ($input['latitude'] ?? ''));
|
||||
$longitude = trim((string) ($input['longitude'] ?? ''));
|
||||
$elevation = trim((string) ($input['elevation'] ?? ''));
|
||||
$timezone = trim((string) ($input['timezone'] ?? 'Europe/Berlin'));
|
||||
|
||||
if ($name === '') {
|
||||
$errors[] = 'Bitte einen Namen für den Standort eingeben.';
|
||||
}
|
||||
|
||||
if ($latitude === '' || !is_numeric($latitude)) {
|
||||
$errors[] = 'Bitte einen gültigen Breitengrad eingeben.';
|
||||
} else {
|
||||
$lat = (float) $latitude;
|
||||
if ($lat < -90 || $lat > 90) {
|
||||
$errors[] = 'Der Breitengrad muss zwischen -90 und 90 liegen.';
|
||||
}
|
||||
}
|
||||
|
||||
if ($longitude === '' || !is_numeric($longitude)) {
|
||||
$errors[] = 'Bitte einen gültigen Längengrad eingeben.';
|
||||
} else {
|
||||
$lon = (float) $longitude;
|
||||
if ($lon < -180 || $lon > 180) {
|
||||
$errors[] = 'Der Längengrad muss zwischen -180 und 180 liegen.';
|
||||
}
|
||||
}
|
||||
|
||||
if ($elevation !== '' && !is_numeric($elevation)) {
|
||||
$errors[] = 'Die Höhe muss numerisch sein.';
|
||||
}
|
||||
|
||||
if (!validTimezone($timezone)) {
|
||||
$errors[] = 'Bitte eine gültige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'elevation' => $elevation,
|
||||
'timezone' => $timezone,
|
||||
];
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
@@ -130,7 +177,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
|
||||
|
||||
if ($satelliteId <= 0) {
|
||||
$errors[] = 'Ungueltiger Satellit.';
|
||||
$errors[] = 'Ungültiger Satellit.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -178,7 +225,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
|
||||
|
||||
if ($satelliteId <= 0) {
|
||||
$errors[] = 'Ungueltiger Satellit.';
|
||||
$errors[] = 'Ungültiger Satellit.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -203,7 +250,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (empty($errors)) {
|
||||
$satelliteCount = countUserRows($pdo, 'app_user_satellites', $userId);
|
||||
if ($satelliteCount <= 1) {
|
||||
$errors[] = 'Der letzte Satellit kann nicht geloescht werden.';
|
||||
$errors[] = 'Der letzte Satellit kann nicht gelöscht werden.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +319,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
|
||||
|
||||
if ($satelliteId <= 0) {
|
||||
$errors[] = 'Ungueltiger Satellit.';
|
||||
$errors[] = 'Ungültiger Satellit.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -314,7 +361,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
|
||||
|
||||
if ($minorplanetId <= 0) {
|
||||
$errors[] = 'Ungueltiger Kleinplanet.';
|
||||
$errors[] = 'Ungültiger Kleinplanet.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -362,7 +409,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
|
||||
|
||||
if ($minorplanetId <= 0) {
|
||||
$errors[] = 'Ungueltiger Kleinplanet.';
|
||||
$errors[] = 'Ungültiger Kleinplanet.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -384,7 +431,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
|
||||
|
||||
if ($minorplanetId <= 0) {
|
||||
$errors[] = 'Ungueltiger Kleinplanet.';
|
||||
$errors[] = 'Ungültiger Kleinplanet.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -405,11 +452,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$isDefault = isset($_POST['is_default']) ? 1 : 0;
|
||||
|
||||
if ($name === '') {
|
||||
$errors[] = 'Bitte einen Namen fuer den Standort eingeben.';
|
||||
$errors[] = 'Bitte einen Namen für den Standort eingeben.';
|
||||
}
|
||||
|
||||
if ($latitude === '' || !is_numeric($latitude)) {
|
||||
$errors[] = 'Bitte einen gueltigen Breitengrad eingeben.';
|
||||
$errors[] = 'Bitte einen gültigen Breitengrad eingeben.';
|
||||
} else {
|
||||
$lat = (float) $latitude;
|
||||
if ($lat < -90 || $lat > 90) {
|
||||
@@ -418,11 +465,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if ($longitude === '' || !is_numeric($longitude)) {
|
||||
$errors[] = 'Bitte einen gueltigen Laengengrad eingeben.';
|
||||
$errors[] = 'Bitte einen gültigen Längengrad eingeben.';
|
||||
} else {
|
||||
$lon = (float) $longitude;
|
||||
if ($lon < -180 || $lon > 180) {
|
||||
$errors[] = 'Der Laengengrad muss zwischen -180 und 180 liegen.';
|
||||
$errors[] = 'Der Längengrad muss zwischen -180 und 180 liegen.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +478,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if (!validTimezone($timezone)) {
|
||||
$errors[] = 'Bitte eine gueltige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
|
||||
$errors[] = 'Bitte eine gültige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -494,11 +541,90 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'update_location') {
|
||||
$locationId = (int) ($_POST['location_id'] ?? 0);
|
||||
$locationForm = validateLocationForm($_POST, $errors);
|
||||
$setDefault = isset($_POST['is_default']) ? 1 : 0;
|
||||
|
||||
if ($locationId <= 0) {
|
||||
$errors[] = 'Ungültiger Standort.';
|
||||
}
|
||||
|
||||
$existingLocation = null;
|
||||
if (empty($errors)) {
|
||||
$stmtLocation = $pdo->prepare("
|
||||
SELECT `id`, `is_default`
|
||||
FROM `app_user_locations`
|
||||
WHERE `id` = :id
|
||||
AND `user_id` = :user_id
|
||||
LIMIT 1
|
||||
");
|
||||
$stmtLocation->execute([
|
||||
':id' => $locationId,
|
||||
':user_id' => $userId,
|
||||
]);
|
||||
$existingLocation = $stmtLocation->fetch();
|
||||
|
||||
if (!$existingLocation) {
|
||||
$errors[] = 'Der Standort wurde nicht gefunden.';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
if ($setDefault === 1) {
|
||||
$stmtReset = $pdo->prepare("
|
||||
UPDATE `app_user_locations`
|
||||
SET `is_default` = 0
|
||||
WHERE `user_id` = :user_id
|
||||
");
|
||||
$stmtReset->execute([
|
||||
':user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$stmtUpdate = $pdo->prepare("
|
||||
UPDATE `app_user_locations`
|
||||
SET
|
||||
`name` = :name,
|
||||
`latitude` = :latitude,
|
||||
`longitude` = :longitude,
|
||||
`elevation` = :elevation,
|
||||
`timezone` = :timezone,
|
||||
`is_default` = :is_default,
|
||||
`updated_at` = NOW()
|
||||
WHERE `id` = :id
|
||||
AND `user_id` = :user_id
|
||||
");
|
||||
$stmtUpdate->execute([
|
||||
':id' => $locationId,
|
||||
':user_id' => $userId,
|
||||
':name' => $locationForm['name'],
|
||||
':latitude' => (float) $locationForm['latitude'],
|
||||
':longitude' => (float) $locationForm['longitude'],
|
||||
':elevation' => $locationForm['elevation'] !== '' ? (float) $locationForm['elevation'] : null,
|
||||
':timezone' => $locationForm['timezone'],
|
||||
':is_default' => $setDefault === 1 ? 1 : (int) ($existingLocation['is_default'] ?? 0),
|
||||
]);
|
||||
|
||||
$pdo->commit();
|
||||
$successMessage = 'Der Standort wurde aktualisiert.';
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$errors[] = 'Der Standort konnte nicht aktualisiert werden.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'set_default_location') {
|
||||
$locationId = (int) ($_POST['location_id'] ?? 0);
|
||||
|
||||
if ($locationId <= 0) {
|
||||
$errors[] = 'Ungueltiger Standort.';
|
||||
$errors[] = 'Ungültiger Standort.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -558,7 +684,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$locationId = (int) ($_POST['location_id'] ?? 0);
|
||||
|
||||
if ($locationId <= 0) {
|
||||
$errors[] = 'Ungueltiger Standort.';
|
||||
$errors[] = 'Ungültiger Standort.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -583,7 +709,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (empty($errors)) {
|
||||
$locationCount = countUserRows($pdo, 'app_user_locations', $userId);
|
||||
if ($locationCount <= 1) {
|
||||
$errors[] = 'Der letzte Standort kann nicht geloescht werden.';
|
||||
$errors[] = 'Der letzte Standort kann nicht gelöscht werden.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,12 +764,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
$successMessage = 'Der Standort wurde geloescht.';
|
||||
$successMessage = 'Der Standort wurde gelöscht.';
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$errors[] = 'Der Standort konnte nicht geloescht werden.';
|
||||
$errors[] = 'Der Standort konnte nicht gelöscht werden.';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -857,7 +983,7 @@ $loggedIn = true;
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Breitengrad</th>
|
||||
<th>Laengengrad</th>
|
||||
<th>Längengrad</th>
|
||||
<th>Hoehe</th>
|
||||
<th>Zeitzone</th>
|
||||
<th>Standard</th>
|
||||
@@ -874,6 +1000,17 @@ $loggedIn = true;
|
||||
<td><?= h($location['timezone']) ?></td>
|
||||
<td><?= (int) $location['is_default'] === 1 ? '*' : '-' ?></td>
|
||||
<td class="action-cell">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary js-edit-location"
|
||||
data-location-id="<?= (int) $location['id'] ?>"
|
||||
data-location-name="<?= h((string) $location['name']) ?>"
|
||||
data-location-latitude="<?= h((string) $location['latitude']) ?>"
|
||||
data-location-longitude="<?= h((string) $location['longitude']) ?>"
|
||||
data-location-elevation="<?= h($location['elevation'] !== null ? (string) $location['elevation'] : '') ?>"
|
||||
data-location-timezone="<?= h((string) $location['timezone']) ?>"
|
||||
data-location-default="<?= (int) $location['is_default'] === 1 ? '1' : '0' ?>"
|
||||
>Bearbeiten</button>
|
||||
<?php if ((int) $location['is_default'] !== 1): ?>
|
||||
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standard-Standort wird gesetzt...">
|
||||
<input type="hidden" name="action" value="set_default_location">
|
||||
@@ -882,10 +1019,10 @@ $loggedIn = true;
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php if (count($locations) > 1): ?>
|
||||
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standort wird entfernt..." onsubmit="return confirm('Standort wirklich loeschen?');">
|
||||
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standort wird entfernt..." onsubmit="return confirm('Standort wirklich löschen?');">
|
||||
<input type="hidden" name="action" value="delete_location">
|
||||
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Loeschen</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="hint">Letzter Standort</span>
|
||||
@@ -911,12 +1048,25 @@ $loggedIn = true;
|
||||
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
<form method="post" action="" class="form-stack mt-xl favorites-loading-form" data-loading-message="Standort wird gespeichert...">
|
||||
<form method="post" action="" id="add-location-form" class="form-stack mt-xl favorites-loading-form" data-loading-message="Standort wird gespeichert...">
|
||||
<input type="hidden" name="action" value="add_location">
|
||||
<div class="grid-2 gap-sm">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" id="name" name="name" required value="<?= h($_POST['name'] ?? '') ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
aria-describedby="add-location-name-error"
|
||||
value="<?= h($_POST['name'] ?? '') ?>"
|
||||
>
|
||||
<p
|
||||
id="add-location-name-error"
|
||||
class="favorites-loading-hint favorites-loading-hint-error"
|
||||
data-validation-hint="name"
|
||||
<?= (($_POST['action'] ?? '') === 'add_location' && trim((string) ($_POST['name'] ?? '')) === '') ? '' : 'hidden' ?>
|
||||
>Bitte einen Namen für den Standort eingeben.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="timezone">Zeitzone (IANA)</label>
|
||||
@@ -927,7 +1077,7 @@ $loggedIn = true;
|
||||
<input type="text" id="latitude" name="latitude" required value="<?= h($_POST['latitude'] ?? ($defaultLocation['latitude'] ?? '')) ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="longitude">Laengengrad</label>
|
||||
<label for="longitude">Längengrad</label>
|
||||
<input type="text" id="longitude" name="longitude" required value="<?= h($_POST['longitude'] ?? ($defaultLocation['longitude'] ?? '')) ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -940,11 +1090,50 @@ $loggedIn = true;
|
||||
Als Standard-Standort speichern
|
||||
</label>
|
||||
<button type="submit" class="btn btn-primary">Standort speichern</button>
|
||||
<p class="favorites-loading-hint" hidden>Standort wird gespeichert...</p>
|
||||
<p class="favorites-loading-hint" data-loading-hint hidden>Standort wird gespeichert...</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dialog class="admin-dialog" id="editLocationDialog">
|
||||
<form method="post" action="" class="admin-dialog-card form-stack favorites-loading-form" data-loading-message="Standort wird aktualisiert...">
|
||||
<input type="hidden" name="action" value="update_location">
|
||||
<input type="hidden" name="location_id" id="edit-location-id" value="">
|
||||
<h3>Standort bearbeiten</h3>
|
||||
<div class="grid-2 gap-sm">
|
||||
<div class="form-group">
|
||||
<label for="edit-location-name">Name</label>
|
||||
<input type="text" id="edit-location-name" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-location-timezone">Zeitzone (IANA)</label>
|
||||
<input type="text" id="edit-location-timezone" name="timezone" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-location-latitude">Breitengrad</label>
|
||||
<input type="text" id="edit-location-latitude" name="latitude" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-location-longitude">Längengrad</label>
|
||||
<input type="text" id="edit-location-longitude" name="longitude" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit-location-elevation">Höhe (optional, Meter)</label>
|
||||
<input type="text" id="edit-location-elevation" name="elevation">
|
||||
</div>
|
||||
</div>
|
||||
<label class="form-checkbox">
|
||||
<input type="checkbox" id="edit-location-default" name="is_default" value="1">
|
||||
Als Standard-Standort speichern
|
||||
</label>
|
||||
<div class="admin-dialog-actions">
|
||||
<button type="button" class="btn btn-secondary" id="editLocationCancel">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Änderungen speichern</button>
|
||||
</div>
|
||||
<p class="favorites-loading-hint" data-loading-hint hidden>Standort wird aktualisiert...</p>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<h2>Satelliten suchen</h2>
|
||||
@@ -959,7 +1148,7 @@ $loggedIn = true;
|
||||
>
|
||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||
</div>
|
||||
<p class="favorites-loading-hint" hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
|
||||
<p class="favorites-loading-hint" data-loading-hint hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
|
||||
</form>
|
||||
|
||||
<?php if ($satelliteSearchQuery !== ''): ?>
|
||||
@@ -1014,7 +1203,7 @@ $loggedIn = true;
|
||||
>
|
||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||
</div>
|
||||
<p class="favorites-loading-hint" hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
|
||||
<p class="favorites-loading-hint" data-loading-hint hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
|
||||
</form>
|
||||
|
||||
<?php if ($minorplanetSearchQuery !== ''): ?>
|
||||
@@ -1061,7 +1250,7 @@ $loggedIn = true;
|
||||
<div class="card">
|
||||
<h2>Meine Satelliten</h2>
|
||||
<?php if (empty($trackedSatellites)): ?>
|
||||
<p class="hint">Du hast noch keine Satelliten ausgewaehlt.</p>
|
||||
<p class="hint">Du hast noch keine Satelliten ausgewählt.</p>
|
||||
<?php else: ?>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
@@ -1112,7 +1301,7 @@ $loggedIn = true;
|
||||
<div class="card">
|
||||
<h2>Meine Kleinplaneten</h2>
|
||||
<?php if (empty($trackedMinorplanets)): ?>
|
||||
<p class="hint">Du hast noch keine Kleinplaneten ausgewaehlt.</p>
|
||||
<p class="hint">Du hast noch keine Kleinplaneten ausgewählt.</p>
|
||||
<?php else: ?>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
@@ -1221,7 +1410,7 @@ $loggedIn = true;
|
||||
const lon = parseFloat(lonInput.value);
|
||||
|
||||
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
|
||||
alert('Bitte zuerst einen gueltigen Standort waehlen.');
|
||||
alert('Bitte zuerst einen gültigen Standort wählen.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1336,13 +1525,40 @@ $loggedIn = true;
|
||||
})();
|
||||
|
||||
document.querySelectorAll('.favorites-loading-form').forEach((form) => {
|
||||
form.addEventListener('submit', () => {
|
||||
form.addEventListener('submit', (event) => {
|
||||
if (form.id === 'add-location-form') {
|
||||
const nameField = form.querySelector('#name');
|
||||
const nameError = document.getElementById('add-location-name-error');
|
||||
|
||||
if (nameField) {
|
||||
const trimmedName = nameField.value.trim();
|
||||
|
||||
if (trimmedName === '') {
|
||||
event.preventDefault();
|
||||
nameField.value = '';
|
||||
nameField.focus();
|
||||
|
||||
if (nameError) {
|
||||
nameError.textContent = 'Bitte einen Namen für den Standort eingeben.';
|
||||
nameError.hidden = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nameError) {
|
||||
nameError.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const message = form.dataset.loadingMessage || 'Bitte warten...';
|
||||
let hint = form.querySelector('.favorites-loading-hint');
|
||||
let hint = form.querySelector('[data-loading-hint]');
|
||||
|
||||
if (!hint) {
|
||||
hint = document.createElement('p');
|
||||
hint.className = 'favorites-loading-hint';
|
||||
hint.dataset.loadingHint = 'true';
|
||||
form.appendChild(hint);
|
||||
}
|
||||
|
||||
@@ -1361,6 +1577,40 @@ document.querySelectorAll('.favorites-loading-form').forEach((form) => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
(function () {
|
||||
const dialog = document.getElementById('editLocationDialog');
|
||||
const cancelButton = document.getElementById('editLocationCancel');
|
||||
|
||||
if (!dialog || typeof dialog.showModal !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const idInput = document.getElementById('edit-location-id');
|
||||
const nameInput = document.getElementById('edit-location-name');
|
||||
const latitudeInput = document.getElementById('edit-location-latitude');
|
||||
const longitudeInput = document.getElementById('edit-location-longitude');
|
||||
const elevationInput = document.getElementById('edit-location-elevation');
|
||||
const timezoneInput = document.getElementById('edit-location-timezone');
|
||||
const defaultInput = document.getElementById('edit-location-default');
|
||||
|
||||
document.querySelectorAll('.js-edit-location').forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
idInput.value = button.dataset.locationId || '';
|
||||
nameInput.value = button.dataset.locationName || '';
|
||||
latitudeInput.value = button.dataset.locationLatitude || '';
|
||||
longitudeInput.value = button.dataset.locationLongitude || '';
|
||||
elevationInput.value = button.dataset.locationElevation || '';
|
||||
timezoneInput.value = button.dataset.locationTimezone || 'Europe/Berlin';
|
||||
defaultInput.checked = button.dataset.locationDefault === '1';
|
||||
dialog.showModal();
|
||||
});
|
||||
});
|
||||
|
||||
cancelButton?.addEventListener('click', () => {
|
||||
dialog.close();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/footer.php'; ?>
|
||||
|
||||
@@ -32,13 +32,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<h2>Kein Standard-Standort gesetzt</h2>
|
||||
<p class="hint">
|
||||
Diese Seite verwendet den im Header geladenen Standard-Standort aus der Datenbank.
|
||||
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
</p>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
<section class="card planetvis-card">
|
||||
<p class="planetvis-intro">
|
||||
Sichtbarkeit der Planeten und des Mondes ueber 24 Stunden fuer deinen Standard-Standort aus der Datenbank.
|
||||
Sichtbarkeit der Planeten und des Mondes über 24 Stunden für deinen Standard-Standort aus der Datenbank.
|
||||
</p>
|
||||
|
||||
<div class="date-section">
|
||||
@@ -533,7 +533,7 @@ function renderChart(localDate) {
|
||||
document.getElementById('tt-body').innerHTML =
|
||||
`Uhrzeit: ${hStr(h)}<br>Aufgang: ${rStr}<br>Untergang: ${sStr}<br>` +
|
||||
(def.type === 'moon' ? `Beleuchtung: ${Math.round(def.illum * 100)}%<br>` : `Magnitude: ${mag}<br>`) +
|
||||
`Status: ${above ? '<span class="tt-up">ueber Horizont</span>' : '<span class="tt-down">unter Horizont</span>'}`;
|
||||
`Status: ${above ? '<span class="tt-up">über Horizont</span>' : '<span class="tt-down">unter Horizont</span>'}`;
|
||||
tooltip.style.left = (e.clientX + 16) + 'px';
|
||||
tooltip.style.top = (e.clientY - 14) + 'px';
|
||||
tooltip.classList.add('vis');
|
||||
@@ -551,7 +551,7 @@ function renderChart(localDate) {
|
||||
infoDiv.innerHTML = `
|
||||
<div class="info-card"><strong>Standort</strong><br>${SITE_LOCATION.name}<br>${LAT.toFixed(3)}deg N · ${LNG.toFixed(3)}deg O · ${ELEV} m</div>
|
||||
<div class="info-card"><strong>Sonne (${fmtDate(localDate)})</strong><br>Aufgang: ${hF(tw.sunRise)}<br>Untergang: ${hF(tw.sunSet)}</div>
|
||||
<div class="info-card"><strong>Daemmerung</strong><br>Buergerlich: ${hF(tw.civDawn)} / ${hF(tw.civDusk)}<br>Nautisch: ${hF(tw.nautDawn)} / ${hF(tw.nautDusk)}<br>Astronomisch: ${hF(tw.astroDawn)} / ${hF(tw.astroDusk)}</div>
|
||||
<div class="info-card"><strong>Dämmerung</strong><br>Bürgerlich: ${hF(tw.civDawn)} / ${hF(tw.civDusk)}<br>Nautisch: ${hF(tw.nautDawn)} / ${hF(tw.nautDusk)}<br>Astronomisch: ${hF(tw.astroDawn)} / ${hF(tw.astroDusk)}</div>
|
||||
<div class="info-card"><strong>Mond (${fmtDate(localDate)})</strong><br>${Math.round(moonInfo.illum * 100)}% ${moonInfo.waning ? 'abnehmend' : 'zunehmend'}<br>Phase: ${moonInfo.phase.toFixed(1)}deg</div>
|
||||
<div class="info-card"><strong>Zeitzone</strong><br>${tzH === 2 ? 'MESZ (UTC+2)' : 'MEZ (UTC+1)'}<br>${SITE_LOCATION.timezone}</div>
|
||||
`;
|
||||
|
||||
+1
-1
@@ -3121,7 +3121,7 @@ def classify_visibility(sun_alt_deg: float) -> dict:
|
||||
if sun_alt_deg > -5:
|
||||
return {"key": "day", "label": "Tag"}
|
||||
if sun_alt_deg >= -10:
|
||||
return {"key": "twilight", "label": "Daemmerung"}
|
||||
return {"key": "twilight", "label": "Dämmerung"}
|
||||
return {"key": "night", "label": "Nacht"}
|
||||
|
||||
|
||||
|
||||
+84
-41
@@ -39,6 +39,54 @@ function h(?string $value): string
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function getPythonCandidates(): array
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
return ['python', 'py'];
|
||||
}
|
||||
|
||||
return ['/usr/bin/python3', '/usr/bin/python', 'python3', 'python'];
|
||||
}
|
||||
|
||||
function runPythonCommand(string $scriptPath, array $args): array
|
||||
{
|
||||
putenv('PYTHONUTF8=1');
|
||||
putenv('PYTHONIOENCODING=utf-8');
|
||||
|
||||
$escapedArguments = array_map('escapeshellarg', array_merge([$scriptPath], $args));
|
||||
$lastError = 'Python konnte nicht gestartet werden.';
|
||||
|
||||
foreach (getPythonCandidates() as $pythonBinary) {
|
||||
$command = $pythonBinary . ' ' . implode(' ', $escapedArguments) . ' 2>&1';
|
||||
$output = [];
|
||||
$resultCode = 0;
|
||||
exec($command, $output, $resultCode);
|
||||
|
||||
$joined = trim(implode("\n", $output));
|
||||
$decoded = json_decode($joined, true);
|
||||
|
||||
if (is_array($decoded)) {
|
||||
return [
|
||||
'ok' => (bool) ($decoded['ok'] ?? false),
|
||||
'data' => $decoded,
|
||||
'raw' => $joined,
|
||||
'command' => $command,
|
||||
];
|
||||
}
|
||||
|
||||
if ($joined !== '') {
|
||||
$lastError = $joined;
|
||||
} elseif ($resultCode !== 0) {
|
||||
$lastError = 'Fehlercode ' . $resultCode . ' bei ' . $pythonBinary;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => false,
|
||||
'error' => $lastError,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Satelliten aus DB laden ──────────────────────────────────────────────────
|
||||
|
||||
function tleChecksum(string $line): int
|
||||
@@ -291,34 +339,29 @@ if (!empty($satellitePayload) && !empty($defaultLocation['latitude'])) {
|
||||
$windowStart = $dayStart->setTimezone(new DateTimeZone('UTC'))->format(DateTime::ATOM);
|
||||
$windowEnd = $dayEnd->setTimezone(new DateTimeZone('UTC'))->format(DateTime::ATOM);
|
||||
|
||||
$scriptPath = realpath(__DIR__ . '/py/api.py');
|
||||
$satellitesJson = json_encode($satellitePayload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$args = [
|
||||
'/usr/bin/python',
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg('satellite_passes'),
|
||||
escapeshellarg((string)$lat),
|
||||
escapeshellarg((string)$lon),
|
||||
escapeshellarg((string)$elev),
|
||||
escapeshellarg($tz),
|
||||
escapeshellarg($windowStart),
|
||||
escapeshellarg($windowEnd),
|
||||
escapeshellarg($satellitesJson),
|
||||
];
|
||||
|
||||
$command = implode(' ', $args) . ' 2>&1';
|
||||
$output = [];
|
||||
$code = 0;
|
||||
exec($command, $output, $code);
|
||||
|
||||
$joined = implode('', $output);
|
||||
$decoded = json_decode($joined, true);
|
||||
|
||||
if (is_array($decoded) && ($decoded['ok'] ?? false)) {
|
||||
$pythonResult = $decoded;
|
||||
$scriptPath = realpath(__DIR__ . '/py/api.py');
|
||||
if ($scriptPath === false) {
|
||||
$pythonError = 'Das Python-Skript für den Satellitenhimmel wurde nicht gefunden.';
|
||||
} else {
|
||||
$pythonError = $joined ?: 'Leere Antwort von Python.';
|
||||
$satellitesJson = json_encode($satellitePayload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
$satellitesPayload = base64_encode((string)$satellitesJson);
|
||||
|
||||
$pythonCall = runPythonCommand($scriptPath, [
|
||||
'satellite_passes',
|
||||
(string)$lat,
|
||||
(string)$lon,
|
||||
(string)$elev,
|
||||
$tz,
|
||||
$windowStart,
|
||||
$windowEnd,
|
||||
$satellitesPayload,
|
||||
]);
|
||||
|
||||
if ($pythonCall['ok'] ?? false) {
|
||||
$pythonResult = $pythonCall['data'];
|
||||
} else {
|
||||
$pythonError = $pythonCall['raw'] ?? $pythonCall['error'] ?? 'Leere Antwort von Python.';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -340,8 +383,8 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<div class="satpass-tool">
|
||||
<section class="card satpass-card">
|
||||
<p class="satpass-intro">
|
||||
Berechnet fuer den heutigen Tag den naechsten Ueberflug deiner ausgewaehlten Satelliten
|
||||
und zeichnet deren Bahn auf einer Zenitkarte fuer deinen Standard-Standort.
|
||||
Berechnet für den heutigen Tag den nächsten Überflug deiner ausgewählten Satelliten
|
||||
und zeichnet deren Bahn auf einer Zenitkarte für deinen Standard-Standort.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -349,14 +392,14 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<section class="card satpass-card">
|
||||
<h2>Kein Standard-Standort gesetzt</h2>
|
||||
<p class="hint">
|
||||
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
</p>
|
||||
</section>
|
||||
<?php elseif (empty($satellitePayload)): ?>
|
||||
<section class="card satpass-card">
|
||||
<h2>Keine Satelliten ausgewaehlt</h2>
|
||||
<h2>Keine Satelliten ausgewählt</h2>
|
||||
<p class="hint">
|
||||
Es sind noch keine ausgewaehlten Satelliten mit aktuellem TLE vorhanden.
|
||||
Es sind noch keine ausgewählten Satelliten mit aktuellem TLE vorhanden.
|
||||
Bitte fuege in <a href="my_favorites.php">Favoriten</a> Satelliten hinzu.
|
||||
</p>
|
||||
</section>
|
||||
@@ -392,13 +435,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<div class="form-group">
|
||||
<label for="satSelect">Satellit</label>
|
||||
<select id="satSelect" class="satpass-select">
|
||||
<option value="">– Satellit waehlen –</option>
|
||||
<option value="">– Satellit wählen –</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="passSelect">Ueberflug</label>
|
||||
<label for="passSelect">Überflug</label>
|
||||
<select id="passSelect" class="satpass-select" disabled>
|
||||
<option value="">– zuerst Satellit waehlen –</option>
|
||||
<option value="">– zuerst Satellit wählen –</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="satpassPassInfo" class="satpass-pass-info"></div>
|
||||
@@ -653,15 +696,15 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
}
|
||||
|
||||
function buildObservationSummary(satName, pass, visibility) {
|
||||
if (visibility.sky.key === 'day') return `${satName} ist bei diesem Ueberflug tagsueber nicht zu sehen.`;
|
||||
if (visibility.sky.key === 'day') return `${satName} ist bei diesem Überflug tagsüber nicht zu sehen.`;
|
||||
const visiblePhase = getVisiblePhase(pass);
|
||||
const pathText = buildPathDescription(visiblePhase.visiblePath);
|
||||
const shadowText = pass.shadowEntry
|
||||
? ` Sie tritt um ${formatTime(pass.shadowEntry.time)} Uhr in den Erdschatten ein.` : '';
|
||||
const magText = pass.peakMag !== null
|
||||
? ` Ihre maximale Helligkeit betraegt ${formatMag(pass.peakMag)} mag.` : '';
|
||||
return `Die Rahmenbedingungen fuer die Beobachtung von ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr sind ${visibility.label}. `
|
||||
+ `Der Ueberflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. `
|
||||
return `Die Rahmenbedingungen für die Beobachtung von ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr sind ${visibility.label}. `
|
||||
+ `Der Überflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. `
|
||||
+ `${pathText} Die maximale Horizonthoehe betraegt ${pass.peakElevation.toFixed(0)}°.`
|
||||
+ `${shadowText}${magText}`;
|
||||
}
|
||||
@@ -910,7 +953,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
timeZone: SITE_LOCATION.timezone, day:'2-digit', month:'2-digit', year:'numeric'
|
||||
}).format(new Date(WINDOW_END)) : '';
|
||||
|
||||
satSelectEl.innerHTML = '<option value="">– Satellit waehlen –</option>';
|
||||
satSelectEl.innerHTML = '<option value="">– Satellit wählen –</option>';
|
||||
PYTHON_RESULTS.forEach((sat, satIdx) => {
|
||||
const upcomingCount = sat.passes.filter(p => p.losTime > now).length;
|
||||
const opt = document.createElement('option');
|
||||
@@ -925,13 +968,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
passInfoEl.innerHTML = '';
|
||||
if (isNaN(satIdx)) {
|
||||
passSelectEl.disabled = true;
|
||||
passSelectEl.innerHTML = '<option value="">– zuerst Satellit waehlen –</option>';
|
||||
passSelectEl.innerHTML = '<option value="">– zuerst Satellit wählen –</option>';
|
||||
drawSky([], null, false);
|
||||
return;
|
||||
}
|
||||
const sat = PYTHON_RESULTS[satIdx];
|
||||
passSelectEl.disabled = false;
|
||||
passSelectEl.innerHTML = '<option value="">– Ueberflug waehlen –</option>';
|
||||
passSelectEl.innerHTML = '<option value="">– Überflug wählen –</option>';
|
||||
sat.passes.forEach((p, passIdx) => {
|
||||
const isPast = p.losTime <= now;
|
||||
const opt = document.createElement('option');
|
||||
|
||||
+7
-7
@@ -80,7 +80,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
|
||||
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Bitte eine gueltige E-Mail-Adresse eingeben.';
|
||||
$errors[] = 'Bitte eine gültige E-Mail-Adresse eingeben.';
|
||||
}
|
||||
|
||||
if ($displayName !== '' && mb_strlen($displayName) > 100) {
|
||||
@@ -163,7 +163,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
':id' => $userId,
|
||||
]);
|
||||
|
||||
$successMessage = 'Die Telegram-Verknuepfung wurde entfernt.';
|
||||
$successMessage = 'Die Telegram-Verknüpfung wurde entfernt.';
|
||||
}
|
||||
|
||||
if ($action === 'update_tcrb_telegram_notifications') {
|
||||
@@ -217,7 +217,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if ($newPassword !== $newPasswordRepeat) {
|
||||
$errors[] = 'Die neuen Passwoerter stimmen nicht ueberein.';
|
||||
$errors[] = 'Die neuen Passwörter stimmen nicht überein.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
@@ -311,7 +311,7 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
|
||||
<h2>Telegram verbinden</h2>
|
||||
<div class="form-stack">
|
||||
<p class="hint">
|
||||
Spaetere Bot-Nachrichten funktionieren nur mit einer verknuepften Chat-ID. Der Telegram-Username allein reicht Telegram fuer aktive Nachrichten an einen Nutzer nicht aus.
|
||||
Spätere Bot-Nachrichten funktionieren nur mit einer verknüpften Chat-ID. Der Telegram-Username allein reicht Telegram für aktive Nachrichten an einen Nutzer nicht aus.
|
||||
</p>
|
||||
<table class="data-table mb-lg">
|
||||
<tr><td>Status</td><td><?= $telegramHasConnection ? 'Verbunden' : 'Nicht verbunden' ?></td></tr>
|
||||
@@ -336,9 +336,9 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
|
||||
</form>
|
||||
|
||||
<?php if ($telegramHasConnection): ?>
|
||||
<form method="post" action="" onsubmit="return confirm('Telegram-Verknuepfung wirklich entfernen?');">
|
||||
<form method="post" action="" onsubmit="return confirm('Telegram-Verknüpfung wirklich entfernen?');">
|
||||
<input type="hidden" name="action" value="disconnect_telegram">
|
||||
<button type="submit" class="btn btn-danger">Verknuepfung loesen</button>
|
||||
<button type="submit" class="btn btn-danger">Verknüpfung lösen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -351,7 +351,7 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<p class="hint">
|
||||
Verbindung starten: <a href="<?= h($publicBasePath . 'telegram/connect.php') ?>">Telegram-Verknuepfung oeffnen</a>
|
||||
Verbindung starten: <a href="<?= h($publicBasePath . 'telegram/connect.php') ?>">Telegram-Verknüpfung öffnen</a>
|
||||
<?php if ($telegramDeepLink !== null): ?>
|
||||
oder direkt im Bot:
|
||||
<a href="<?= h($telegramDeepLink) ?>" target="_blank" rel="noopener">@<?= h($telegramBotUsername) ?></a>
|
||||
|
||||
@@ -1246,7 +1246,6 @@ async function initSolarSystem() {
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x03050c, 0.0018);
|
||||
|
||||
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 2000);
|
||||
planetMeshes = new Map();
|
||||
|
||||
@@ -46,7 +46,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
|
||||
$escapedArguments[] = escapeshellarg((string)$argument);
|
||||
}
|
||||
|
||||
$lastError = 'Python konnte nicht ausgefuehrt werden.';
|
||||
$lastError = 'Python konnte nicht ausgeführt werden.';
|
||||
$debugOutput = [];
|
||||
|
||||
foreach ($pythonCandidates as $pythonBinary) {
|
||||
@@ -65,7 +65,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
|
||||
|
||||
$decoded = json_decode($joined, true);
|
||||
if (!is_array($decoded)) {
|
||||
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gueltiges JSON.' : 'Python lieferte keine Ausgabe zurueck.';
|
||||
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gültiges JSON.' : 'Python lieferte keine Ausgabe zurück.';
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<h2>Kein Standard-Standort gesetzt</h2>
|
||||
<p class="hint">
|
||||
Fuer die Berechnung wird der Standard-Standort aus der Datenbank verwendet.
|
||||
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
|
||||
</p>
|
||||
</section>
|
||||
<?php elseif ($pageError !== null): ?>
|
||||
@@ -371,12 +371,12 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="occultWeekBtn">7 Tage</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" id="occultMonthBtn">Monat</button>
|
||||
</form>
|
||||
<span class="hint">Die Berechnung laeuft im Hintergrund. Hoehere Grenzgroessen brauchen deutlich laenger.</span>
|
||||
<span class="hint">Die Berechnung läuft im Hintergrund. Hoehere Grenzgrössen brauchen deutlich länger.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="occult-status occult-status-hidden" id="occultSubmitStatus" hidden>
|
||||
<span class="occult-spinner occult-spinner-visible" aria-hidden="true"></span>
|
||||
<span>Berechnung laeuft... Die serverseitige Python-Auswertung kann je nach Grenzgroesse etwas dauern.</span>
|
||||
<span>Berechnung laeuft... Die Auswertung kann je nach Grenzgrösse etwas dauern.</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -384,7 +384,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<section class="card occult-card">
|
||||
<h2>Bereit zur Berechnung</h2>
|
||||
<p class="hint">
|
||||
Die Seite bleibt jetzt waehrend der Berechnung sichtbar. Waehle Datum und Grenzgroesse und starte die
|
||||
Wähle Datum und Grenzgröße und starte die
|
||||
Auswertung mit dem Button.
|
||||
</p>
|
||||
</section>
|
||||
@@ -399,7 +399,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<div class="occult-detail-head-controls">
|
||||
<div class="occult-detail-meta" id="occultDetailMeta">Feld 5 x 5 Grad, Mond im Zentrum</div>
|
||||
<div class="occult-map-size-row">
|
||||
<label for="occultMapSizeSelect" class="occult-select-label">Kartengroesse</label>
|
||||
<label for="occultMapSizeSelect" class="occult-select-label">Kartengrösse</label>
|
||||
<select id="occultMapSizeSelect" class="satpass-select occult-select">
|
||||
<option value="5" selected>5 x 5 Grad</option>
|
||||
<option value="10">10 x 10 Grad</option>
|
||||
@@ -432,7 +432,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
|
||||
<div class="occult-submit-progress-bar" aria-hidden="true">
|
||||
<div class="occult-submit-progress-fill" id="occultSubmitProgressFill"></div>
|
||||
</div>
|
||||
<div class="occult-submit-progress-note">Geschaetzter Fortschritt waehrend der laufenden Serverberechnung.</div>
|
||||
<div class="occult-submit-progress-note">Geschätzter Fortschritt während der laufenden Serverberechnung.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -717,27 +717,6 @@ function drawArc(){
|
||||
ctx.clip();
|
||||
|
||||
if(!sd.polarNight){
|
||||
// Unter-Horizont gestrichelt (normal day) - nur Punkte wo alt<0
|
||||
if(!sd.polarDay){
|
||||
const sub=[];
|
||||
for(let i=0;i<=80;i++){
|
||||
const h=sd.riseUTC-3+i*6/80;
|
||||
const{altitude,azimuth}=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,h));
|
||||
if(altitude<0) sub.push(proj(altitude,azimuth));
|
||||
else if(sub.length>0) { sub.push(null); } // Unterbrecher
|
||||
}
|
||||
// Zeichne mit Unterbrechungen
|
||||
ctx.beginPath();
|
||||
let inPath=false;
|
||||
sub.forEach(p=>{
|
||||
if(!p){inPath=false;return;}
|
||||
if(!inPath){ctx.moveTo(p.x,p.y);inPath=true;}
|
||||
else ctx.lineTo(p.x,p.y);
|
||||
});
|
||||
ctx.strokeStyle='rgba(200,168,75,.15)';ctx.lineWidth=1.5*dpr;
|
||||
ctx.setLineDash([4*dpr,8*dpr]);ctx.stroke();ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
// Äußerer Gluehschein
|
||||
ctx.beginPath();
|
||||
drawWrappedPath(ctx,pts,W,sd.polarDay);
|
||||
|
||||
@@ -185,9 +185,12 @@ PS
|
||||
|
||||
$body = @file_get_contents($url, false, $context);
|
||||
$statusCode = 0;
|
||||
$responseHeaders = function_exists('http_get_last_response_headers')
|
||||
? http_get_last_response_headers()
|
||||
: ($http_response_header ?? null);
|
||||
|
||||
if (isset($http_response_header) && is_array($http_response_header)) {
|
||||
foreach ($http_response_header as $headerLine) {
|
||||
if (is_array($responseHeaders)) {
|
||||
foreach ($responseHeaders as $headerLine) {
|
||||
if (preg_match('/^HTTP\/\S+\s+(\d{3})\b/', $headerLine, $matches) === 1) {
|
||||
$statusCode = (int) $matches[1];
|
||||
break;
|
||||
@@ -233,7 +236,7 @@ function sendTelegramMessage(string $botToken, string $chatId, string $messageTe
|
||||
if (!is_array($decoded)) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'description' => 'Telegram hat keine gueltige JSON-Antwort geliefert.',
|
||||
'description' => 'Telegram hat keine gültige JSON-Antwort geliefert.',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -304,7 +307,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if ($action !== 'send_broadcast') {
|
||||
$errors[] = 'Die angeforderte Aktion ist ungueltig.';
|
||||
$errors[] = 'Die angeforderte Aktion ist ungültig.';
|
||||
}
|
||||
|
||||
if ($messageText === '') {
|
||||
@@ -348,7 +351,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
} elseif ($sendMode === 'selected') {
|
||||
if (empty($selectedRecipientIds)) {
|
||||
$errors[] = 'Bitte mindestens einen Empfaenger auswaehlen.';
|
||||
$errors[] = 'Bitte mindestens einen Empfänger auswählen.';
|
||||
} else {
|
||||
foreach ($selectedRecipientIds as $recipientId) {
|
||||
if (isset($recipientIndex[$recipientId])) {
|
||||
@@ -357,13 +360,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
if (empty($targetRecipients)) {
|
||||
$errors[] = 'Die ausgewaehlten Empfaenger konnten nicht geladen werden.';
|
||||
$errors[] = 'Die ausgewählten Empfänger konnten nicht geladen werden.';
|
||||
}
|
||||
}
|
||||
} elseif ($sendMode === 'all') {
|
||||
$targetRecipients = $recipients;
|
||||
} else {
|
||||
$errors[] = 'Der Versandmodus ist ungueltig.';
|
||||
$errors[] = 'Der Versandmodus ist ungültig.';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +422,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
</p>
|
||||
|
||||
<table class="data-table mb-lg">
|
||||
<tr><td>Verknuepfte Master-Konten</td><td><?= count($recipients) ?></td></tr>
|
||||
<tr><td>Verknüpfte Master-Konten</td><td><?= count($recipients) ?></td></tr>
|
||||
<tr><td>Bot</td><td><?= h(($telegramConfig['bot_username'] ?? '') !== '' ? '@' . (string) $telegramConfig['bot_username'] : 'nicht konfiguriert') ?></td></tr>
|
||||
</table>
|
||||
|
||||
@@ -428,14 +431,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['telegram_broadcast_csrf']) ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Empfaenger</label>
|
||||
<label>Empfänger</label>
|
||||
<label>
|
||||
<input type="radio" name="send_mode" value="self" <?= $sendMode === 'self' ? 'checked' : '' ?>>
|
||||
Nur an mich senden
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="send_mode" value="selected" <?= $sendMode === 'selected' ? 'checked' : '' ?>>
|
||||
An ausgewaehlte Nutzer senden
|
||||
An ausgewählte Nutzer senden
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="send_mode" value="all" <?= $sendMode === 'all' ? 'checked' : '' ?>>
|
||||
@@ -444,7 +447,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="recipient_ids">Auswahl fuer "ausgewaehlte Nutzer"</label>
|
||||
<label for="recipient_ids">Auswahl für "ausgewählte Nutzer"</label>
|
||||
<select id="recipient_ids" name="recipient_ids[]" multiple size="8">
|
||||
<?php foreach ($recipients as $recipient): ?>
|
||||
<?php $recipientId = (int) $recipient['id']; ?>
|
||||
@@ -473,7 +476,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Empfaenger</h2>
|
||||
<h2>Empfänger</h2>
|
||||
<?php if (empty($recipients)): ?>
|
||||
<p class="hint">Noch keine verknuepften Master-Konten vorhanden.</p>
|
||||
<?php else: ?>
|
||||
|
||||
@@ -36,7 +36,7 @@ $publicBasePath = '../';
|
||||
$pageTitle = 'Telegram verbinden - AstroTools';
|
||||
$headerIntroPre = 'Konto';
|
||||
$headerIntroTitle = 'Telegram verbinden';
|
||||
$headerIntroSub = 'Bot-Verknuepfung fuer spaetere Benachrichtigungen.';
|
||||
$headerIntroSub = 'Bot-Verknüpfung für spätere Benachrichtigungen.';
|
||||
|
||||
function h(?string $value): string
|
||||
{
|
||||
@@ -107,7 +107,7 @@ $deepLink = $botUsername !== ''
|
||||
<?php require __DIR__ . '/../header.php'; ?>
|
||||
|
||||
<div class="card">
|
||||
<h2>Telegram-Verknuepfung</h2>
|
||||
<h2>Telegram-Verknüpfung</h2>
|
||||
<p class="hint">
|
||||
Diese Verbindung merkt sich die Chat-ID deines Telegram-Kontos, damit spaeter ein Bot dir Inhalte direkt schicken kann.
|
||||
</p>
|
||||
@@ -122,11 +122,11 @@ $deepLink = $botUsername !== ''
|
||||
|
||||
<?php if ($deepLink !== null): ?>
|
||||
<p>
|
||||
<a class="btn btn-primary" href="<?= h($deepLink) ?>" target="_blank" rel="noopener">Bot in Telegram oeffnen</a>
|
||||
<a class="btn btn-primary" href="<?= h($deepLink) ?>" target="_blank" rel="noopener">Bot in Telegram öffnen</a>
|
||||
</p>
|
||||
<div class="telegram-qr-block">
|
||||
<div class="telegram-qr-image" id="telegramConnectQrLarge" data-telegram-link="<?= h($deepLink) ?>"></div>
|
||||
<p class="hint">QR-Code mit dem Handy scannen. Telegram oeffnet dann direkt den richtigen Bot mit deinem Verknuepfungslink.</p>
|
||||
<p class="hint">QR-Code mit dem Handy scannen. Telegram öffnet dann direkt den richtigen Bot mit deinem Verknüpfungslink.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="hint">
|
||||
@@ -144,7 +144,7 @@ $deepLink = $botUsername !== ''
|
||||
</div>
|
||||
|
||||
<p class="hint">
|
||||
Nach erfolgreicher Verknuepfung kannst du zurueck zu <a href="<?= h($publicBasePath . 'settings.php') ?>">Einstellungen</a>.
|
||||
Nach erfolgreicher Verknüpfung kannst du zurück zu <a href="<?= h($publicBasePath . 'settings.php') ?>">Einstellungen</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user