index sonne und Mond rein und Sat Überflüge
This commit is contained in:
@@ -16,7 +16,33 @@
|
||||
"Bash(php --version)",
|
||||
"Bash(/c/Users/ich/Downloads/php/php -S localhost:8080 -t public)",
|
||||
"Bash(/c/Users/ich/Downloads/php/php -m)",
|
||||
"Bash(/c/Users/ich/Downloads/php/php --ini)"
|
||||
"Bash(/c/Users/ich/Downloads/php/php --ini)",
|
||||
"Bash(php -S localhost:8080 -t c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public)",
|
||||
"Bash(where php:*)",
|
||||
"Bash(/e/php/php.exe -S localhost:8080 -t \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public\")",
|
||||
"Bash(/e/php/php.exe -m)",
|
||||
"Read(//e/php/ext//**)",
|
||||
"Bash(/e/php/php.exe --ini)",
|
||||
"Bash(sed -i 's|^;extension_dir = \"ext\"|extension_dir = \"ext\"|' /e/php/php.ini)",
|
||||
"Bash(sed -i 's|^;extension=pdo_mysql|extension=pdo_mysql|' /e/php/php.ini)",
|
||||
"Bash(sed -i 's|^;extension=mysqli|extension=mysqli|' /e/php/php.ini)",
|
||||
"Bash(taskkill //F //IM php.exe)",
|
||||
"Bash(/usr/bin/python3 --version)",
|
||||
"Bash(/usr/bin/python --version)",
|
||||
"Read(//c/Users/HP/AppData/Local/Programs/**)",
|
||||
"Bash(ls /c/Python*/python.exe)",
|
||||
"Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe --version)",
|
||||
"Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/py/api.py sun_moon_rise_set 50.35 11.17 360 2026-04-01 Europe/Berlin)",
|
||||
"Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe -c \"from zoneinfo import ZoneInfo; ZoneInfo\\('Europe/Berlin'\\)\")",
|
||||
"Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe -m pip install tzdata)",
|
||||
"Bash(/e/php/php.exe -r ':*)",
|
||||
"Bash(/e/php/php.exe -r \"echo ini_get\\('disable_functions'\\);\")",
|
||||
"Bash(/e/php/php.exe -S localhost:8080 -r \"echo ini_get\\('disable_functions'\\);\")",
|
||||
"Bash(cp /tmp/debug_exec.php \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/debug_exec.php\")",
|
||||
"Bash(/e/php/php.exe -l \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/index.php\")",
|
||||
"Bash(/e/php/php.exe \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/index.php\")",
|
||||
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:8080/index.php)",
|
||||
"Bash(curl -s http://localhost:8080/index.php)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,6 +828,58 @@ footer a:hover { color: var(--gold); }
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
/* Kompakte Auf-/Untergangs-Übersicht (Startseite) */
|
||||
.riseset-card { display: inline-block; }
|
||||
|
||||
.riseset-grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
column-gap: 1.2rem;
|
||||
row-gap: 0.18rem;
|
||||
align-items: baseline;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.riseset-label { color: var(--text-dim); white-space: nowrap; }
|
||||
.riseset-val { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
|
||||
.riseset-main.riseset-label,
|
||||
.riseset-main.riseset-val {
|
||||
color: var(--gold-pale);
|
||||
font-weight: 600;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.riseset-moon.riseset-label,
|
||||
.riseset-moon.riseset-val {
|
||||
color: var(--cyan);
|
||||
font-weight: 600;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.riseset-dim.riseset-label,
|
||||
.riseset-dim.riseset-val {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.riseset-sep {
|
||||
grid-column: 1 / -1;
|
||||
height: 0;
|
||||
border-top: 1px solid var(--border);
|
||||
margin: 0.3rem 0;
|
||||
}
|
||||
|
||||
.sat-passes-grid {
|
||||
grid-template-columns: auto auto auto;
|
||||
}
|
||||
|
||||
.sat-passes-grid small {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.75em;
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
|
||||
/* --- Aktions-Zelle (Buttons in Tabellenzeilen) ------------ */
|
||||
|
||||
.action-cell {
|
||||
|
||||
+295
-66
@@ -1,58 +1,236 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
session_start();
|
||||
|
||||
$loggedIn = isset($_SESSION['user_id']);
|
||||
|
||||
$defaultLocation = null;
|
||||
|
||||
if ($loggedIn) {
|
||||
$config = require __DIR__ . '/../config/database.php';
|
||||
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;dbname=%s;charset=%s',
|
||||
$config['host'],
|
||||
$config['dbname'],
|
||||
$config['charset'] ?? 'utf8mb4'
|
||||
);
|
||||
|
||||
$pdo = new PDO(
|
||||
$dsn,
|
||||
$config['user'],
|
||||
$config['pass'],
|
||||
[
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]
|
||||
);
|
||||
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$displayName = $_SESSION['display_name'] ?? '';
|
||||
$username = $_SESSION['username'] ?? '';
|
||||
$role = $_SESSION['role'] ?? 'user';
|
||||
|
||||
$stmtLocation = $pdo->prepare("
|
||||
SELECT
|
||||
`name`,
|
||||
`latitude`,
|
||||
`longitude`,
|
||||
`elevation`,
|
||||
`timezone`
|
||||
FROM `app_user_locations`
|
||||
WHERE `user_id` = :user_id
|
||||
AND `is_default` = 1
|
||||
LIMIT 1
|
||||
");
|
||||
$stmtLocation->execute([':user_id' => $userId]);
|
||||
$defaultLocation = $stmtLocation->fetch();
|
||||
if (!function_exists('h')) {
|
||||
function h(?string $value): string { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); }
|
||||
}
|
||||
if (!function_exists('risesetTime')) {
|
||||
function risesetTime(array $ev, string $key): string { return (!empty($ev[$key]['found'])) ? htmlspecialchars($ev[$key]['local_time'], ENT_QUOTES, 'UTF-8') : '—'; }
|
||||
}
|
||||
if (!function_exists('fmtLocalTime')) {
|
||||
function fmtLocalTime(string $utcIso, string $tz): string { return (new DateTime($utcIso))->setTimezone(new DateTimeZone($tz))->format('H:i'); }
|
||||
}
|
||||
if (!function_exists('azDir')) {
|
||||
function azDir(float $az): string { $dirs = ['N','NO','O','SO','S','SW','W','NW']; return $dirs[(int)round($az / 45) % 8]; }
|
||||
}
|
||||
|
||||
function h(?string $value): string
|
||||
function idx_tleChecksum(string $line): int
|
||||
{
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
$sum = 0;
|
||||
for ($i = 0; $i < 68; $i++) {
|
||||
$c = $line[$i];
|
||||
if (ctype_digit($c)) $sum += (int)$c;
|
||||
elseif ($c === '-') $sum += 1;
|
||||
}
|
||||
return $sum % 10;
|
||||
}
|
||||
function idx_tleExp(float $val): string
|
||||
{
|
||||
if (abs($val) < 1e-99) return ' 00000-0';
|
||||
$sign = $val >= 0 ? '+' : '-';
|
||||
$abs = abs($val);
|
||||
$exp = (int)floor(log10($abs)) + 1;
|
||||
$mantissa = (int)round($abs / pow(10, $exp) * 1e5);
|
||||
if ($mantissa >= 100000) { $mantissa = 10000; $exp++; }
|
||||
return sprintf('%s%05d%s%d', $sign, $mantissa, $exp >= 0 ? '+' : '-', abs($exp));
|
||||
}
|
||||
function idx_tleMmDot(float $val): string
|
||||
{
|
||||
return sprintf('%s.%08d', $val >= 0 ? ' ' : '-', (int)round(abs($val) * 1e8));
|
||||
}
|
||||
function idx_tleEpoch(string $epochStr): string
|
||||
{
|
||||
$dt = new DateTimeImmutable($epochStr, new DateTimeZone('UTC'));
|
||||
$yy = (int)$dt->format('Y') % 100;
|
||||
$jan1 = new DateTimeImmutable($dt->format('Y') . '-01-01 00:00:00', new DateTimeZone('UTC'));
|
||||
$day = ((float)$dt->format('U') - (float)$jan1->format('U')) / 86400.0 + 1.0;
|
||||
return sprintf('%02d%012.8f', $yy, $day);
|
||||
}
|
||||
function idx_tleIntlDesig(?string $id): string
|
||||
{
|
||||
if (!$id) return str_repeat(' ', 8);
|
||||
if (preg_match('/^(\d{4})-(\d+)([A-Z]*)$/i', trim($id), $m)) {
|
||||
$yy = substr($m[1], 2, 2);
|
||||
return substr(str_pad($yy . str_pad($m[2], 3, '0', STR_PAD_LEFT) . str_pad(strtoupper($m[3]), 3), 8), 0, 8);
|
||||
}
|
||||
return str_pad(substr($id, 0, 8), 8);
|
||||
}
|
||||
function buildTleLine1(array $sat, array $tle): string
|
||||
{
|
||||
$line = sprintf('1 %05d%1s %-8s %14s %10s %8s %8s %1d %4d',
|
||||
(int)$sat['norad_cat_id'],
|
||||
in_array($sat['classification_type'] ?? '', ['U','C','S'], true) ? $sat['classification_type'] : 'U',
|
||||
idx_tleIntlDesig($sat['object_id'] ?? null),
|
||||
idx_tleEpoch((string)$tle['epoch']),
|
||||
idx_tleMmDot((float)($tle['mean_motion_dot'] ?? 0)),
|
||||
idx_tleExp((float)($tle['mean_motion_ddot'] ?? 0)),
|
||||
idx_tleExp((float)($tle['bstar'] ?? 0)),
|
||||
(int)($tle['ephemeris_type'] ?? 0),
|
||||
(int)($tle['element_set_no'] ?? 0)
|
||||
);
|
||||
$line = str_pad($line, 68);
|
||||
return $line . idx_tleChecksum($line);
|
||||
}
|
||||
function buildTleLine2(array $sat, array $tle): string
|
||||
{
|
||||
$ecc = (float)($tle['eccentricity'] ?? 0);
|
||||
$line = sprintf('2 %05d %8.4f %8.4f %7s %8.4f %8.4f %11.8f%5d',
|
||||
(int)$sat['norad_cat_id'],
|
||||
(float)($tle['inclination'] ?? 0),
|
||||
(float)($tle['ra_of_asc_node'] ?? 0),
|
||||
substr(sprintf('%.7f', $ecc), 2),
|
||||
(float)($tle['arg_of_pericenter'] ?? 0),
|
||||
(float)($tle['mean_anomaly'] ?? 0),
|
||||
(float)($tle['mean_motion'] ?? 0),
|
||||
(int)($tle['rev_at_epoch'] ?? 0)
|
||||
);
|
||||
$line = str_pad($line, 68);
|
||||
return $line . idx_tleChecksum($line);
|
||||
}
|
||||
|
||||
// Standort und astronomische Daten nur wenn eingeloggt und Standort vorhanden
|
||||
$skyData = null;
|
||||
$planetData = null;
|
||||
$satData = null;
|
||||
$pyError = null;
|
||||
|
||||
if ($loggedIn) {
|
||||
// header.php lädt $currentLocation – wir brauchen ihn vorab
|
||||
$config = require __DIR__ . '/../config/database.php';
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;dbname=%s;charset=%s', $config['host'], $config['dbname'], $config['charset'] ?? 'utf8mb4'),
|
||||
$config['user'],
|
||||
$config['pass'],
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||||
);
|
||||
|
||||
$stmtLoc = $pdo->prepare(
|
||||
'SELECT name, latitude, longitude, elevation, timezone
|
||||
FROM app_user_locations WHERE user_id = ? AND is_default = 1 LIMIT 1'
|
||||
);
|
||||
$stmtLoc->execute([(int)$_SESSION['user_id']]);
|
||||
$defaultLocation = $stmtLoc->fetch() ?: null;
|
||||
|
||||
if ($defaultLocation) {
|
||||
$lat = (float)$defaultLocation['latitude'];
|
||||
$lon = (float)$defaultLocation['longitude'];
|
||||
$elev = (float)($defaultLocation['elevation'] ?? 0);
|
||||
$tz = $defaultLocation['timezone'] ?? 'Europe/Berlin';
|
||||
$date = date('Y-m-d');
|
||||
|
||||
$scriptPath = realpath(__DIR__ . '/py/api.py');
|
||||
if (file_exists('/usr/bin/python3')) {
|
||||
$pythonBin = '/usr/bin/python3';
|
||||
} elseif (file_exists('/usr/bin/python')) {
|
||||
$pythonBin = '/usr/bin/python';
|
||||
} else {
|
||||
$pythonBin = 'C:\\Users\\HP\\AppData\\Local\\Python\\bin\\python.exe';
|
||||
}
|
||||
putenv('PYTHONUTF8=1');
|
||||
putenv('PYTHONIOENCODING=utf-8');
|
||||
|
||||
// Sonne + Mond
|
||||
$args = [
|
||||
$pythonBin,
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg('sun_moon_rise_set'),
|
||||
escapeshellarg((string)$lat),
|
||||
escapeshellarg((string)$lon),
|
||||
escapeshellarg((string)$elev),
|
||||
escapeshellarg($date),
|
||||
escapeshellarg($tz),
|
||||
];
|
||||
$cmd = implode(' ', $args) . ' 2>&1';
|
||||
$out = [];
|
||||
exec($cmd, $out);
|
||||
$raw = json_decode(implode('', $out), true);
|
||||
if (is_array($raw) && ($raw['ok'] ?? false)) {
|
||||
$skyData = $raw;
|
||||
} else {
|
||||
$pyError = $raw['error'] ?? 'Fehler beim Berechnen der Aufgangszeiten.';
|
||||
}
|
||||
|
||||
// Planeten
|
||||
$args2 = [
|
||||
$pythonBin,
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg('planet_rise_set'),
|
||||
escapeshellarg((string)$lat),
|
||||
escapeshellarg((string)$lon),
|
||||
escapeshellarg((string)$elev),
|
||||
escapeshellarg($date),
|
||||
escapeshellarg($tz),
|
||||
];
|
||||
$cmd2 = implode(' ', $args2) . ' 2>&1';
|
||||
$out2 = [];
|
||||
exec($cmd2, $out2);
|
||||
$raw2 = json_decode(implode('', $out2), true);
|
||||
if (is_array($raw2) && ($raw2['ok'] ?? false)) {
|
||||
$planetData = $raw2;
|
||||
} elseif ($pyError === null) {
|
||||
$pyError = $raw2['error'] ?? 'Fehler beim Berechnen der Planetenzeiten.';
|
||||
}
|
||||
|
||||
// Satelliten-Überflüge heute — Favorit zuerst, dann alle gemerkten
|
||||
$stmtSat = $pdo->prepare(
|
||||
'SELECT s.id, s.norad_cat_id, s.object_name, s.object_type,
|
||||
s.object_id, s.classification_type,
|
||||
t.epoch, t.mean_motion, t.eccentricity, t.inclination,
|
||||
t.ra_of_asc_node, t.arg_of_pericenter, t.mean_anomaly,
|
||||
t.ephemeris_type, t.element_set_no, t.rev_at_epoch,
|
||||
t.bstar, t.mean_motion_dot, t.mean_motion_ddot,
|
||||
us.is_favorite
|
||||
FROM app_user_satellites us
|
||||
JOIN sat_satellites s ON s.id = us.satellite_id
|
||||
JOIN sat_tle_current t ON t.satellite_id = s.id
|
||||
WHERE us.user_id = ?
|
||||
AND t.epoch IS NOT NULL
|
||||
ORDER BY us.is_favorite DESC, s.object_name ASC
|
||||
LIMIT 3'
|
||||
);
|
||||
$stmtSat->execute([(int)$_SESSION['user_id']]);
|
||||
$userSats = $stmtSat->fetchAll();
|
||||
|
||||
if ($userSats) {
|
||||
$tzObj = new DateTimeZone($tz);
|
||||
$dayStart = new DateTime('today', $tzObj);
|
||||
$dayStart->setTimezone(new DateTimeZone('UTC'));
|
||||
$dayEnd = new DateTime('tomorrow', $tzObj);
|
||||
$dayEnd->setTimezone(new DateTimeZone('UTC'));
|
||||
$satsJson = json_encode(array_map(fn($r) => [
|
||||
'id' => $r['id'],
|
||||
'norad_cat_id' => $r['norad_cat_id'],
|
||||
'object_name' => $r['object_name'],
|
||||
'tle_line1' => buildTleLine1($r, $r),
|
||||
'tle_line2' => buildTleLine2($r, $r),
|
||||
'is_favorite' => (bool)$r['is_favorite'],
|
||||
], $userSats), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$args3 = [
|
||||
$pythonBin,
|
||||
escapeshellarg($scriptPath),
|
||||
escapeshellarg('satellite_passes'),
|
||||
escapeshellarg((string)$lat),
|
||||
escapeshellarg((string)$lon),
|
||||
escapeshellarg((string)$elev),
|
||||
escapeshellarg($tz),
|
||||
escapeshellarg($dayStart->format('Y-m-d\TH:i:s\Z')),
|
||||
escapeshellarg($dayEnd->format('Y-m-d\TH:i:s\Z')),
|
||||
escapeshellarg($satsJson),
|
||||
];
|
||||
$cmd3 = implode(' ', $args3) . ' 2>&1';
|
||||
$out3 = [];
|
||||
exec($cmd3, $out3);
|
||||
$raw3 = json_decode(implode('', $out3), true);
|
||||
if (is_array($raw3) && ($raw3['ok'] ?? false)) {
|
||||
$satData = $raw3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php require __DIR__ . '/header.php'; ?>
|
||||
@@ -70,36 +248,87 @@ function h(?string $value): string
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<p class="welcome-text">
|
||||
Willkommen zurück, <strong><?= h($displayName !== '' ? $displayName : $username) ?></strong>.
|
||||
<?php if (!$defaultLocation): ?>
|
||||
<p class="hint">Noch kein Standort hinterlegt. <a href="settings.php">Jetzt anlegen</a></p>
|
||||
<?php elseif ($pyError): ?>
|
||||
<ul class="msg-error"><li><?= h($pyError) ?></li></ul>
|
||||
<?php else: ?>
|
||||
|
||||
<p class="hint rise-set-location">
|
||||
Aufgangs- und Untergangszeiten für
|
||||
<strong><?= h($defaultLocation['name']) ?></strong>
|
||||
— <?= h(date('d.m.Y')) ?>
|
||||
</p>
|
||||
|
||||
<div class="grid-2">
|
||||
<?php $ev = $skyData['events'] ?? []; ?>
|
||||
<div class="card riseset-card">
|
||||
<h2>Heute</h2>
|
||||
<div class="riseset-grid">
|
||||
<span class="riseset-label riseset-main">☀ Sonne</span>
|
||||
<span class="riseset-val riseset-main">↑ <?= risesetTime($ev, 'sunrise') ?></span>
|
||||
<span class="riseset-val riseset-main">↓ <?= risesetTime($ev, 'sunset') ?></span>
|
||||
|
||||
<div class="card">
|
||||
<h2>Konto</h2>
|
||||
<table class="data-table">
|
||||
<tr><td>Benutzername</td><td><?= h($username) ?></td></tr>
|
||||
<tr><td>Rolle</td><td><?= h($role) ?></td></tr>
|
||||
</table>
|
||||
<span class="riseset-label riseset-dim">Bürgerl.</span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'civil_dawn') ?></span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'civil_dusk') ?></span>
|
||||
|
||||
<span class="riseset-label riseset-dim">Nautisch</span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'nautical_dawn') ?></span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'nautical_dusk') ?></span>
|
||||
|
||||
<span class="riseset-label riseset-dim">Astronom.</span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'astro_dawn') ?></span>
|
||||
<span class="riseset-val riseset-dim"><?= risesetTime($ev, 'astro_dusk') ?></span>
|
||||
|
||||
<span class="riseset-sep"></span><span class="riseset-sep"></span><span class="riseset-sep"></span>
|
||||
|
||||
<span class="riseset-label riseset-moon">☽ Mond</span>
|
||||
<span class="riseset-val riseset-moon">↑ <?= risesetTime($ev, 'moonrise') ?></span>
|
||||
<span class="riseset-val riseset-moon">↓ <?= risesetTime($ev, 'moonset') ?></span>
|
||||
|
||||
<span class="riseset-sep"></span><span class="riseset-sep"></span><span class="riseset-sep"></span>
|
||||
|
||||
<?php foreach (($planetData['planets'] ?? []) as $planet): ?>
|
||||
<span class="riseset-label"><?= h($planet['name']) ?></span>
|
||||
<span class="riseset-val">↑ <?= ($planet['rise']['found'] ?? false) ? h($planet['rise']['local_time']) : '—' ?></span>
|
||||
<span class="riseset-val">↓ <?= ($planet['set']['found'] ?? false) ? h($planet['set']['local_time']) : '—' ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Standard-Standort</h2>
|
||||
<?php if ($defaultLocation): ?>
|
||||
<table class="data-table">
|
||||
<tr><td>Name</td><td><?= h($defaultLocation['name']) ?></td></tr>
|
||||
<tr><td>Breitengrad</td><td><?= h((string)$defaultLocation['latitude']) ?></td></tr>
|
||||
<tr><td>Längengrad</td><td><?= h((string)$defaultLocation['longitude']) ?></td></tr>
|
||||
<tr><td>Höhe</td><td><?= h($defaultLocation['elevation'] !== null ? (string)$defaultLocation['elevation'] . ' m' : '—') ?></td></tr>
|
||||
<tr><td>Zeitzone</td><td><?= h($defaultLocation['timezone']) ?></td></tr>
|
||||
</table>
|
||||
<?php if ($satData): ?>
|
||||
<?php
|
||||
$favName = '';
|
||||
$favPasses = [];
|
||||
foreach (($satData['results'] ?? []) as $sat) {
|
||||
if ($sat['is_favorite']) {
|
||||
$favName = $sat['name'];
|
||||
$favPasses = $sat['passes'] ?? [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
usort($favPasses, fn($a, $b) => strcmp($a['aos_utc'], $b['aos_utc']));
|
||||
?>
|
||||
<div class="card riseset-card">
|
||||
<h2>☆ <?= h($favName ?: 'Favorit') ?></h2>
|
||||
<?php if (empty($favPasses)): ?>
|
||||
<p class="hint">Heute keine Überflüge.</p>
|
||||
<?php else: ?>
|
||||
<p class="hint">Noch kein Standort hinterlegt. <a href="settings.php">Jetzt anlegen</a></p>
|
||||
<div class="riseset-grid sat-passes-grid">
|
||||
<span class="riseset-label riseset-dim">AOS</span>
|
||||
<span class="riseset-label riseset-dim">LOS</span>
|
||||
<span class="riseset-label riseset-dim">Max</span>
|
||||
<?php foreach ($favPasses as $p): ?>
|
||||
<span class="riseset-val riseset-main"><?= fmtLocalTime($p['aos_utc'], $tz) ?> <small><?= $p['aos_az'] !== null ? azDir((float)$p['aos_az']) : '' ?></small></span>
|
||||
<span class="riseset-val"><?= fmtLocalTime($p['los_utc'], $tz) ?> <small><?= $p['los_az'] !== null ? azDir((float)$p['los_az']) : '' ?></small></span>
|
||||
<span class="riseset-val riseset-dim"><?= round((float)$p['peak_el']) ?>°</span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
Binary file not shown.
+91
-1
@@ -151,6 +151,24 @@ def action_sun_moon_rise_set(args: list[str]) -> dict:
|
||||
moon_rise = search_event(astronomy.Body.Moon, astronomy.Direction.Rise, observer, start_time, utc_end)
|
||||
moon_set = search_event(astronomy.Body.Moon, astronomy.Direction.Set, observer, start_time, utc_end)
|
||||
|
||||
def search_altitude(direction: astronomy.Direction, altitude_deg: float) -> astronomy.Time | None:
|
||||
limit_days = (utc_end - time_to_datetime(start_time)).total_seconds() / 86400.0
|
||||
result = astronomy.SearchAltitude(astronomy.Body.Sun, observer, direction, start_time, limit_days, altitude_deg)
|
||||
if result is None:
|
||||
return None
|
||||
if time_to_datetime(result) >= utc_end:
|
||||
return None
|
||||
return result
|
||||
|
||||
# Morgens: Sonne steigt auf → Direction.Rise (Höhe wird größer)
|
||||
# Abends: Sonne sinkt ab → Direction.Set (Höhe wird kleiner)
|
||||
astro_dawn = search_altitude(astronomy.Direction.Rise, -18.0)
|
||||
nautical_dawn = search_altitude(astronomy.Direction.Rise, -12.0)
|
||||
civil_dawn = search_altitude(astronomy.Direction.Rise, -6.0)
|
||||
civil_dusk = search_altitude(astronomy.Direction.Set, -6.0)
|
||||
nautical_dusk = search_altitude(astronomy.Direction.Set, -12.0)
|
||||
astro_dusk = search_altitude(astronomy.Direction.Set, -18.0)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "sun_moon_rise_set",
|
||||
@@ -168,14 +186,81 @@ def action_sun_moon_rise_set(args: list[str]) -> dict:
|
||||
"utc_end": utc_end.isoformat().replace("+00:00", "Z"),
|
||||
},
|
||||
"events": {
|
||||
"astro_dawn": serialize_event("Astronomische Morgendämmerung", astro_dawn, tz),
|
||||
"nautical_dawn": serialize_event("Nautische Morgendämmerung", nautical_dawn, tz),
|
||||
"civil_dawn": serialize_event("Bürgerliche Morgendämmerung", civil_dawn, tz),
|
||||
"sunrise": serialize_event("Sonnenaufgang", sun_rise, tz),
|
||||
"sunset": serialize_event("Sonnenuntergang", sun_set, tz),
|
||||
"civil_dusk": serialize_event("Bürgerliche Abenddämmerung", civil_dusk, tz),
|
||||
"nautical_dusk": serialize_event("Nautische Abenddämmerung", nautical_dusk, tz),
|
||||
"astro_dusk": serialize_event("Astronomische Abenddämmerung", astro_dusk, tz),
|
||||
"moonrise": serialize_event("Mondaufgang", moon_rise, tz),
|
||||
"moonset": serialize_event("Monduntergang", moon_set, tz),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
PLANETS = [
|
||||
("Merkur", astronomy.Body.Mercury),
|
||||
("Venus", astronomy.Body.Venus),
|
||||
("Mars", astronomy.Body.Mars),
|
||||
("Jupiter", astronomy.Body.Jupiter),
|
||||
("Saturn", astronomy.Body.Saturn),
|
||||
("Uranus", astronomy.Body.Uranus),
|
||||
("Neptun", astronomy.Body.Neptune),
|
||||
]
|
||||
|
||||
|
||||
def action_planet_rise_set(args: list[str]) -> dict:
|
||||
if len(args) != 5:
|
||||
fail(
|
||||
"Aktion planet_rise_set erwartet 5 Argumente: latitude longitude elevation date timezone",
|
||||
extra={"argv": args},
|
||||
)
|
||||
|
||||
latitude = parse_float(args[0], "Latitude")
|
||||
longitude = parse_float(args[1], "Longitude")
|
||||
elevation = parse_float(args[2], "Elevation")
|
||||
date_text = args[3]
|
||||
timezone_name = args[4]
|
||||
|
||||
try:
|
||||
datetime.strptime(date_text, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
fail("Datum muss im Format YYYY-MM-DD uebergeben werden.", extra={"date": date_text})
|
||||
|
||||
try:
|
||||
tz = ZoneInfo(timezone_name)
|
||||
except Exception as exc:
|
||||
fail("Zeitzone ist ungueltig.", extra={"details": str(exc)})
|
||||
|
||||
observer = astronomy.Observer(latitude, longitude, elevation)
|
||||
|
||||
local_start = datetime.strptime(date_text, "%Y-%m-%d").replace(tzinfo=tz)
|
||||
local_end = local_start + timedelta(days=1)
|
||||
utc_start = local_start.astimezone(timezone.utc)
|
||||
utc_end = local_end.astimezone(timezone.utc)
|
||||
start_time = dt_to_time(utc_start)
|
||||
|
||||
planets = []
|
||||
for name, body in PLANETS:
|
||||
rise = search_event(body, astronomy.Direction.Rise, observer, start_time, utc_end)
|
||||
set_ = search_event(body, astronomy.Direction.Set, observer, start_time, utc_end)
|
||||
planets.append({
|
||||
"name": name,
|
||||
"rise": serialize_event(name + "aufgang", rise, tz),
|
||||
"set": serialize_event(name + "untergang", set_, tz),
|
||||
})
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "planet_rise_set",
|
||||
"date": date_text,
|
||||
"timezone": timezone_name,
|
||||
"planets": planets,
|
||||
}
|
||||
|
||||
|
||||
def classify_visibility(sun_alt_deg: float) -> dict:
|
||||
if sun_alt_deg > -5:
|
||||
return {"key": "day", "label": "Tag"}
|
||||
@@ -1201,7 +1286,12 @@ def main() -> None:
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes"]})
|
||||
if action == "planet_rise_set":
|
||||
result = action_planet_rise_set(args)
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set"]})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user