Files
skyview.astronomiemuseum.de/public/monatsvorhersage.php
T

393 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$pageTitle = 'Monatsvorhersage - AstroTools';
$bodyClass = 'admin-page monatsvorhersage-page';
$publicBasePath = './';
$currentYear = (int) date('Y');
$selectedYear = isset($_GET['year']) ? (int) $_GET['year'] : $currentYear;
$selectedMonth = isset($_GET['month']) ? max(1, min(12, (int) $_GET['month'])) : (int) date('n');
$years = range($currentYear - 2, $currentYear + 5);
$months = [
1 => 'Januar',
2 => 'Februar',
3 => 'März',
4 => 'April',
5 => 'Mai',
6 => 'Juni',
7 => 'Juli',
8 => 'August',
9 => 'September',
10 => 'Oktober',
11 => 'November',
12 => 'Dezember',
];
$monthSkyTimes = [
1 => ['hour' => 22, 'minute' => 0],
2 => ['hour' => 22, 'minute' => 0],
3 => ['hour' => 22, 'minute' => 0],
4 => ['hour' => 23, 'minute' => 0],
5 => ['hour' => 23, 'minute' => 0],
6 => ['hour' => 23, 'minute' => 30],
7 => ['hour' => 23, 'minute' => 30],
8 => ['hour' => 23, 'minute' => 0],
9 => ['hour' => 23, 'minute' => 0],
10 => ['hour' => 23, 'minute' => 0],
11 => ['hour' => 22, 'minute' => 0],
12 => ['hour' => 22, 'minute' => 0],
];
$selectedSkyTime = $monthSkyTimes[$selectedMonth] ?? ['hour' => 22, 'minute' => 0];
require __DIR__ . '/header.php';
$chartLocationName = (string) (($currentLocation['name'] ?? null) ?: 'Sternwarte Sonneberg');
$chartLatitude = isset($currentLocation['latitude']) ? (float) $currentLocation['latitude'] : 50.3547;
$chartLongitude = isset($currentLocation['longitude']) ? (float) $currentLocation['longitude'] : 11.1900;
$chartElevation = isset($currentLocation['elevation']) && $currentLocation['elevation'] !== null ? (float) $currentLocation['elevation'] : 640.0;
$chartTimezone = (string) (($currentLocation['timezone'] ?? null) ?: 'Europe/Berlin');
$pythonExecutable = PHP_OS_FAMILY === 'Windows' ? 'python' : '/usr/bin/python';
$pythonScriptPath = __DIR__ . '/py/api.py';
$pythonOutput = [];
$pythonExitCode = 0;
$pythonPayload = null;
$pythonError = null;
$pythonArgs = [
$pythonScriptPath,
'month_sky_context',
(string) $chartLatitude,
(string) $chartLongitude,
(string) $chartElevation,
(string) $selectedYear,
(string) $selectedMonth,
(string) ((int) $selectedSkyTime['hour']),
(string) ((int) $selectedSkyTime['minute']),
$chartTimezone,
];
$command = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $pythonArgs)) . ' 2>&1';
exec($command, $pythonOutput, $pythonExitCode);
$pythonRaw = trim(implode("\n", $pythonOutput));
if ($pythonExitCode !== 0) {
$pythonError = $pythonRaw !== '' ? $pythonRaw : 'Python-Aufruf fehlgeschlagen.';
} else {
$decoded = json_decode($pythonRaw, true);
if (!is_array($decoded)) {
$pythonError = $pythonRaw !== '' ? $pythonRaw : 'Python lieferte kein gültiges JSON.';
} elseif (!($decoded['ok'] ?? false)) {
$pythonError = (string) ($decoded['error'] ?? 'Python meldete einen Fehler.');
} else {
$pythonPayload = $decoded;
}
}
$bodyRows = [];
if (is_array($pythonPayload['bodies'] ?? null)) {
$bodyRows[] = $pythonPayload['bodies']['sun'] ?? null;
$bodyRows[] = $pythonPayload['bodies']['moon'] ?? null;
foreach (($pythonPayload['bodies']['planets'] ?? []) as $planet) {
$bodyRows[] = $planet;
}
$bodyRows = array_values(array_filter($bodyRows, static fn ($row) => is_array($row)));
}
$formatNumber = static function ($value, int $decimals = 1): string {
if (!is_numeric($value)) {
return '–';
}
return number_format((float) $value, $decimals, ',', '');
};
?>
<style>
.monatsvorhersage-shell {
max-width: 1180px;
margin: 0 auto;
display: grid;
gap: 18px;
}
.monatsvorhersage-panel {
background: rgba(7, 12, 22, 0.78);
border: 1px solid rgba(132, 155, 196, 0.18);
border-radius: 18px;
padding: 22px 24px;
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
}
.monatsvorhersage-panel h2,
.monatsvorhersage-panel h3 {
margin: 0 0 10px;
}
.monatsvorhersage-panel p {
margin: 0;
color: rgba(228, 235, 247, 0.82);
line-height: 1.55;
}
.monatsvorhersage-form {
display: flex;
flex-wrap: wrap;
gap: 14px;
align-items: end;
}
.monatsvorhersage-field {
min-width: 180px;
display: grid;
gap: 6px;
}
.monatsvorhersage-field label {
font-size: 0.92rem;
color: rgba(228, 235, 247, 0.76);
}
.monatsvorhersage-field select,
.monatsvorhersage-form button,
.monatsvorhersage-link {
min-height: 44px;
border-radius: 12px;
border: 1px solid rgba(132, 155, 196, 0.26);
background: rgba(12, 20, 34, 0.92);
color: #edf3ff;
padding: 0 14px;
font: inherit;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
}
.monatsvorhersage-form button,
.monatsvorhersage-link {
background: linear-gradient(180deg, rgba(61, 98, 168, 0.95), rgba(40, 69, 122, 0.95));
cursor: pointer;
}
.monatsvorhersage-meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
gap: 12px;
}
.monatsvorhersage-stat {
border-radius: 14px;
padding: 16px 18px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(132, 155, 196, 0.14);
}
.monatsvorhersage-stat strong {
display: block;
margin-bottom: 6px;
color: #f4f7ff;
}
.monatsvorhersage-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 16px;
}
.monatsvorhersage-alert {
padding: 14px 16px;
border-radius: 14px;
border: 1px solid rgba(255, 154, 154, 0.28);
background: rgba(74, 18, 22, 0.52);
color: #ffd7d7;
white-space: pre-wrap;
}
.monatsvorhersage-table-wrap {
overflow-x: auto;
margin-top: 16px;
}
.monatsvorhersage-table {
width: 100%;
border-collapse: collapse;
min-width: 760px;
}
.monatsvorhersage-table th,
.monatsvorhersage-table td {
padding: 11px 10px;
border-bottom: 1px solid rgba(132, 155, 196, 0.12);
text-align: left;
color: rgba(232, 238, 249, 0.9);
font-size: 0.96rem;
}
.monatsvorhersage-table th {
color: rgba(199, 214, 240, 0.8);
font-weight: 600;
}
.monatsvorhersage-pill {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 84px;
padding: 4px 10px;
border-radius: 999px;
font-size: 0.84rem;
font-weight: 600;
}
.monatsvorhersage-pill--up {
background: rgba(87, 188, 138, 0.18);
color: #bff3cf;
}
.monatsvorhersage-pill--down {
background: rgba(133, 152, 184, 0.16);
color: #d5dfef;
}
.monatsvorhersage-list {
margin: 0;
padding-left: 18px;
color: rgba(228, 235, 247, 0.84);
line-height: 1.65;
}
code.monatsvorhersage-code {
font-size: 0.92rem;
}
</style>
<main class="page-content">
<div class="monatsvorhersage-shell">
<section class="monatsvorhersage-panel">
<form class="monatsvorhersage-form" method="get">
<div class="monatsvorhersage-field">
<label for="mv-year">Jahr</label>
<select id="mv-year" name="year">
<?php foreach ($years as $year): ?>
<option value="<?= (int) $year ?>"<?= $year === $selectedYear ? ' selected' : '' ?>><?= (int) $year ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="monatsvorhersage-field">
<label for="mv-month">Monat</label>
<select id="mv-month" name="month">
<?php foreach ($months as $monthNumber => $monthLabel): ?>
<option value="<?= (int) $monthNumber ?>"<?= $monthNumber === $selectedMonth ? ' selected' : '' ?>><?= htmlspecialchars($monthLabel, ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit">Monat laden</button>
</form>
</section>
<section class="monatsvorhersage-panel">
<h2>Serverseitige Monatsbasis</h2>
<p>Diese Vorschau läuft jetzt ohne Browser-Rechenlogik über die Python-AstronomyEngine. Damit ist der erste Baustein für den späteren Cronjob vorhanden: Datum, Standort und Himmelskoordinaten kommen bereits serverseitig aus <code class="monatsvorhersage-code">public/py/api.py</code>.</p>
<div class="monatsvorhersage-meta" style="margin-top:16px;">
<div class="monatsvorhersage-stat">
<strong>Ausgewählter Monat</strong>
<?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?>
</div>
<div class="monatsvorhersage-stat">
<strong>Ziel-Uhrzeit</strong>
<?= sprintf('%02d:%02d Uhr', (int) $selectedSkyTime['hour'], (int) $selectedSkyTime['minute']) ?>
</div>
<div class="monatsvorhersage-stat">
<strong>Standort</strong>
<?= htmlspecialchars($chartLocationName, ENT_QUOTES, 'UTF-8') ?><br>
<?= $formatNumber($chartLatitude, 4) ?>°, <?= $formatNumber($chartLongitude, 4) ?>°
</div>
<div class="monatsvorhersage-stat">
<strong>Zeitzone</strong>
<?= htmlspecialchars($chartTimezone, ENT_QUOTES, 'UTF-8') ?>
</div>
</div>
</section>
<section class="monatsvorhersage-panel">
<h3>Python-Snapshot</h3>
<?php if ($pythonError !== null): ?>
<div class="monatsvorhersage-alert"><?= htmlspecialchars($pythonError, ENT_QUOTES, 'UTF-8') ?></div>
<?php else: ?>
<div class="monatsvorhersage-meta">
<div class="monatsvorhersage-stat">
<strong>Lokalzeit</strong>
<?= htmlspecialchars((string) ($pythonPayload['selected']['local_label'] ?? ''), ENT_QUOTES, 'UTF-8') ?>
</div>
<div class="monatsvorhersage-stat">
<strong>UTC</strong>
<?= htmlspecialchars((string) ($pythonPayload['selected']['utc_iso'] ?? ''), ENT_QUOTES, 'UTF-8') ?>
</div>
<div class="monatsvorhersage-stat">
<strong>Lokale Sternzeit</strong>
<?= $formatNumber($pythonPayload['sidereal']['local_hours'] ?? null, 2) ?> h
</div>
<div class="monatsvorhersage-stat">
<strong>Mondphase</strong>
<?= htmlspecialchars((string) ($pythonPayload['bodies']['moon']['phase_label'] ?? '–'), ENT_QUOTES, 'UTF-8') ?>
</div>
</div>
<div class="monatsvorhersage-table-wrap">
<table class="monatsvorhersage-table">
<thead>
<tr>
<th>Objekt</th>
<th>Höhe</th>
<th>Azimut</th>
<th>RA</th>
<th>Dekl.</th>
<th>Mag.</th>
<th>Sichtbar</th>
</tr>
</thead>
<tbody>
<?php foreach ($bodyRows as $row): ?>
<tr>
<td><?= htmlspecialchars((string) ($row['label'] ?? '–'), ENT_QUOTES, 'UTF-8') ?></td>
<td><?= $formatNumber($row['altitude_deg'] ?? null, 1) ?>°</td>
<td><?= $formatNumber($row['azimuth_deg'] ?? null, 1) ?>°</td>
<td><?= $formatNumber($row['ra_hours'] ?? null, 2) ?> h</td>
<td><?= $formatNumber($row['dec_deg'] ?? null, 1) ?>°</td>
<td><?= $formatNumber($row['magnitude'] ?? null, 1) ?></td>
<td>
<span class="monatsvorhersage-pill <?= !empty($row['visible']) ? 'monatsvorhersage-pill--up' : 'monatsvorhersage-pill--down' ?>">
<?= !empty($row['visible']) ? 'über Horizont' : 'unter Horizont' ?>
</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</section>
<section class="monatsvorhersage-panel">
<h3>Nächste Schritte</h3>
<ol class="monatsvorhersage-list">
<li>Python ergänzt als Nächstes Sterne, Messier-Objekte und Sternbildlinien für genau diesen Monatszeitpunkt.</li>
<li>Danach ziehen wir die Projektionen und das serverseitige Bildrendering nach, damit die Ausgabe cronjob-tauglich als PNG erzeugt werden kann.</li>
<li>Die bisherige Browser-Vorschau bleibt vorerst als fachliche Referenz erhalten.</li>
</ol>
<div class="monatsvorhersage-actions">
<a class="monatsvorhersage-link" href="monatsvorhersage_old.php?year=<?= (int) $selectedYear ?>&amp;month=<?= (int) $selectedMonth ?>">Bisherige Vorschau öffnen</a>
</div>
</section>
</div>
</main>
<?php require __DIR__ . '/footer.php'; ?>