298 lines
16 KiB
PHP
298 lines
16 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
session_start();
|
|
require_once __DIR__ . '/python_api.php';
|
|
|
|
$pageTitle = 'Sonne in Sternbildern - AstroTools';
|
|
$bodyClass = 'sun-constellations-page';
|
|
|
|
function h(string $value): string
|
|
{
|
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function formatSignedDays(float $value): string
|
|
{
|
|
return ($value >= 0 ? '+' : '-') . number_format(abs($value), 2, ',', '.') . ' Tage';
|
|
}
|
|
|
|
$timezoneName = 'Europe/Berlin';
|
|
$currentYear = (int) (new DateTimeImmutable('now', new DateTimeZone($timezoneName)))->format('Y');
|
|
$selectedYear = filter_input(INPUT_GET, 'year', FILTER_VALIDATE_INT);
|
|
if ($selectedYear === false || $selectedYear === null || $selectedYear < 1600 || $selectedYear > 2200) {
|
|
$selectedYear = $currentYear;
|
|
}
|
|
|
|
?>
|
|
<?php require __DIR__ . '/header.php'; ?>
|
|
|
|
<?php
|
|
$pythonResult = runPythonApi('sun_constellation_changes_for_year', [(string) $selectedYear, $timezoneName]);
|
|
$result = $pythonResult['ok'] ? $pythonResult['data'] : null;
|
|
$intervals = is_array($result['intervals'] ?? null) ? $result['intervals'] : [];
|
|
$events = is_array($result['events'] ?? null) ? $result['events'] : [];
|
|
$displayTimezone = new DateTimeZone($timezoneName);
|
|
$intervalsByStart = [];
|
|
foreach ($intervals as $interval) {
|
|
$startIso = (string) ($interval['start_local_iso'] ?? '');
|
|
if ($startIso !== '') {
|
|
$intervalsByStart[$startIso] = $interval;
|
|
}
|
|
}
|
|
$astrologicalStarts = [
|
|
'Steinbock' => [$selectedYear - 1, 12, 22],
|
|
'Wassermann' => [$selectedYear, 1, 20],
|
|
'Fische' => [$selectedYear, 2, 19],
|
|
'Widder' => [$selectedYear, 3, 21],
|
|
'Stier' => [$selectedYear, 4, 20],
|
|
'Zwillinge' => [$selectedYear, 5, 21],
|
|
'Krebs' => [$selectedYear, 6, 21],
|
|
'Löwe' => [$selectedYear, 7, 23],
|
|
'Jungfrau' => [$selectedYear, 8, 23],
|
|
'Waage' => [$selectedYear, 9, 23],
|
|
'Skorpion' => [$selectedYear, 10, 23],
|
|
'Schütze' => [$selectedYear, 11, 22],
|
|
];
|
|
$astrologicalStartsBySymbol = [
|
|
'Cap' => [$selectedYear - 1, 12, 22],
|
|
'Aqr' => [$selectedYear, 1, 20],
|
|
'Psc' => [$selectedYear, 2, 19],
|
|
'Ari' => [$selectedYear, 3, 21],
|
|
'Tau' => [$selectedYear, 4, 20],
|
|
'Gem' => [$selectedYear, 5, 21],
|
|
'Cnc' => [$selectedYear, 6, 21],
|
|
'Leo' => [$selectedYear, 7, 23],
|
|
'Vir' => [$selectedYear, 8, 23],
|
|
'Lib' => [$selectedYear, 9, 23],
|
|
'Sco' => [$selectedYear, 10, 23],
|
|
'Sgr' => [$selectedYear, 11, 22],
|
|
];
|
|
$statConstellations = [
|
|
'Cap' => 'Steinbock',
|
|
'Aqr' => 'Wassermann',
|
|
'Psc' => 'Fische',
|
|
'Ari' => 'Widder',
|
|
'Tau' => 'Stier',
|
|
'Gem' => 'Zwillinge',
|
|
'Cnc' => 'Krebs',
|
|
'Leo' => 'Löwe',
|
|
'Vir' => 'Jungfrau',
|
|
'Lib' => 'Waage',
|
|
'Sco' => 'Skorpion',
|
|
'Sgr' => 'Schütze',
|
|
];
|
|
$selectedStatConstellation = trim((string) ($_GET['stat_constellation'] ?? 'Cap'));
|
|
if (!isset($statConstellations[$selectedStatConstellation])) {
|
|
$selectedStatConstellation = 'Cap';
|
|
}
|
|
$statisticsResult = runPythonApi('sun_constellation_offset_statistics', [
|
|
(string) $selectedYear,
|
|
$selectedStatConstellation,
|
|
$timezoneName,
|
|
]);
|
|
$statisticsData = $statisticsResult['ok'] ? $statisticsResult['data'] : null;
|
|
?>
|
|
|
|
<main class="sun-constellations-tool">
|
|
<section class="card sun-constellations-card">
|
|
<form class="sun-constellations-form" method="get">
|
|
<label for="year">Jahr</label>
|
|
<input id="year" name="year" type="number" min="1600" max="2200" value="<?= h((string) $selectedYear) ?>">
|
|
<button class="btn btn-primary" type="submit">Berechnen</button>
|
|
</form>
|
|
<p class="hint">
|
|
Berechnet werden die offiziellen IAU-Sternbildgrenzen aus geozentrischer Sicht.
|
|
Der Jahreszeitraum beginnt am 01.01.<?= h((string) $selectedYear) ?> um 00:00 Uhr in Europe/Berlin.
|
|
Deshalb erscheint auch der Schlangenträger; der klassische Tierkreis mit zwölf Zeichen ist nicht identisch mit den Sternbildern.
|
|
</p>
|
|
</section>
|
|
|
|
<?php if ($result === null): ?>
|
|
<section class="card msg-error">
|
|
<h2>Berechnung nicht möglich</h2>
|
|
<p><?= h((string) ($pythonResult['error'] ?? 'Unbekannter Python-Fehler.')) ?></p>
|
|
</section>
|
|
<?php else: ?>
|
|
<section class="card sun-constellations-card">
|
|
<h2>Astronomisch und astrologisch</h2>
|
|
<div class="sun-constellations-table-wrap">
|
|
<table class="data-table sun-constellations-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Sternbild</th>
|
|
<th>Wechsel</th>
|
|
<th>Astrologisch</th>
|
|
<th>Differenz</th>
|
|
<th>Von</th>
|
|
<th>Bis</th>
|
|
<th>Dauer</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($events as $event): ?>
|
|
<?php
|
|
$constellationName = (string) ($event['to_constellation_name'] ?? '');
|
|
$interval = $intervalsByStart[(string) ($event['local_iso'] ?? '')] ?? [];
|
|
$astrologicalStart = null;
|
|
$constellationSymbol = (string) ($event['to_constellation_symbol'] ?? '');
|
|
if (isset($astrologicalStartsBySymbol[$constellationSymbol])) {
|
|
[$astrologicalYear, $astrologicalMonth, $astrologicalDay] = $astrologicalStartsBySymbol[$constellationSymbol];
|
|
$astrologicalStart = new DateTimeImmutable(
|
|
sprintf('%04d-%02d-%02d 00:00:00', $astrologicalYear, $astrologicalMonth, $astrologicalDay),
|
|
$displayTimezone
|
|
);
|
|
}
|
|
$astronomicalChange = new DateTimeImmutable((string) ($event['local_iso'] ?? ''), $displayTimezone);
|
|
$differenceText = '-';
|
|
if ($astrologicalStart !== null) {
|
|
$differenceDays = ($astronomicalChange->getTimestamp() - $astrologicalStart->getTimestamp()) / 86400;
|
|
$differenceText = sprintf('%+.1f Tage', $differenceDays);
|
|
}
|
|
?>
|
|
<tr>
|
|
<td><strong><?= h($constellationName) ?></strong></td>
|
|
<td><?= h((string) ($event['local_date'] ?? '')) ?></td>
|
|
<td><?= $astrologicalStart === null ? '-' : h($astrologicalStart->format('d.m.Y')) ?></td>
|
|
<td><?= h($differenceText) ?></td>
|
|
<td><?= h((string) ($interval['start_local_date'] ?? '')) ?>, <?= h((string) ($interval['start_local_time'] ?? '')) ?> Uhr</td>
|
|
<td><?= h((string) ($interval['end_local_date'] ?? '')) ?>, <?= h((string) ($interval['end_local_time'] ?? '')) ?> Uhr</td>
|
|
<td><?= h(number_format((float) ($interval['duration_days'] ?? 0), 1, ',', '.')) ?> Tage</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card sun-constellations-card sun-constellations-statistics">
|
|
<h2>Versatz über 100 Jahre</h2>
|
|
<form class="sun-constellations-form" method="get">
|
|
<input type="hidden" name="year" value="<?= h((string) $selectedYear) ?>">
|
|
<label for="stat_constellation">Sternbild</label>
|
|
<select id="stat_constellation" name="stat_constellation">
|
|
<?php foreach ($statConstellations as $symbol => $label): ?>
|
|
<option value="<?= h($symbol) ?>"<?= $symbol === $selectedStatConstellation ? ' selected' : '' ?>><?= h($label) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<button class="btn btn-primary" type="submit">100 Jahre auswerten</button>
|
|
</form>
|
|
<p class="hint">
|
|
Zeitraum: <?= h((string) $selectedYear) ?> bis <?= h((string) ($selectedYear + 99)) ?>. Der Versatz ist der astronomische Wechselzeitpunkt minus dem üblichen astrologischen Beginn.
|
|
</p>
|
|
|
|
<?php if ($statisticsData === null): ?>
|
|
<p class="msg-error"><?= h((string) ($statisticsResult['error'] ?? 'Statistik konnte nicht berechnet werden.')) ?></p>
|
|
<?php else: ?>
|
|
<?php $statistics = is_array($statisticsData['statistics'] ?? null) ? $statisticsData['statistics'] : []; ?>
|
|
<?php $yearValues = is_array($statisticsData['years'] ?? null) ? $statisticsData['years'] : []; ?>
|
|
<?php
|
|
$chartWidth = 900.0;
|
|
$chartHeight = 270.0;
|
|
$chartLeft = 58.0;
|
|
$chartRight = 20.0;
|
|
$chartTop = 20.0;
|
|
$chartBottom = 42.0;
|
|
$chartValues = array_map(
|
|
static fn(array $yearValue): float => (float) ($yearValue['difference_days'] ?? 0),
|
|
$yearValues
|
|
);
|
|
$chartMin = min($chartValues);
|
|
$chartMax = max($chartValues);
|
|
$chartPadding = max(0.25, ($chartMax - $chartMin) * 0.12);
|
|
$chartLow = $chartMin - $chartPadding;
|
|
$chartHigh = $chartMax + $chartPadding;
|
|
$chartRange = $chartHigh - $chartLow;
|
|
$chartPlotWidth = $chartWidth - $chartLeft - $chartRight;
|
|
$chartPlotHeight = $chartHeight - $chartTop - $chartBottom;
|
|
$chartPoints = [];
|
|
$chartMarkers = [];
|
|
foreach ($yearValues as $index => $yearValue) {
|
|
$value = (float) ($yearValue['difference_days'] ?? 0);
|
|
$x = $chartLeft + ($index / max(1, count($yearValues) - 1)) * $chartPlotWidth;
|
|
$y = $chartTop + (($chartHigh - $value) / $chartRange) * $chartPlotHeight;
|
|
$chartPoints[] = number_format($x, 2, '.', '') . ',' . number_format($y, 2, '.', '');
|
|
if ($index % 10 === 0 || $index === count($yearValues) - 1) {
|
|
$chartMarkers[] = [
|
|
'x' => $x,
|
|
'y' => $y,
|
|
'year' => (string) ($yearValue['year'] ?? ''),
|
|
'value' => $value,
|
|
];
|
|
}
|
|
}
|
|
$movingAverageWindow = 10;
|
|
$movingAverageValues = [];
|
|
$movingAveragePoints = [];
|
|
foreach ($chartValues as $index => $value) {
|
|
if ($index < $movingAverageWindow - 1) {
|
|
continue;
|
|
}
|
|
$movingAverage = array_sum(array_slice($chartValues, $index - $movingAverageWindow + 1, $movingAverageWindow)) / $movingAverageWindow;
|
|
$x = $chartLeft + ($index / max(1, count($yearValues) - 1)) * $chartPlotWidth;
|
|
$y = $chartTop + (($chartHigh - $movingAverage) / $chartRange) * $chartPlotHeight;
|
|
$movingAverageValues[] = $movingAverage;
|
|
$movingAveragePoints[] = number_format($x, 2, '.', '') . ',' . number_format($y, 2, '.', '');
|
|
}
|
|
?>
|
|
<div class="sun-constellations-stats-grid">
|
|
<div><span>10-Jahres-Trend</span><strong><?= h(formatSignedDays((float) ($movingAverageValues[count($movingAverageValues) - 1] ?? 0))) ?></strong></div>
|
|
<div><span>Kleinster Versatz</span><strong><?= h(formatSignedDays((float) ($statistics['min_days'] ?? 0))) ?></strong></div>
|
|
<div><span>Größter Versatz</span><strong><?= h(formatSignedDays((float) ($statistics['max_days'] ?? 0))) ?></strong></div>
|
|
<div><span>Streuung</span><strong><?= h(number_format((float) ($statistics['standard_deviation_days'] ?? 0), 2, ',', '.') . ' Tage') ?></strong></div>
|
|
<div><span>Änderung im Zeitraum</span><strong><?= h(formatSignedDays((float) ($statistics['change_over_period_days'] ?? 0))) ?></strong></div>
|
|
</div>
|
|
|
|
<div class="sun-constellations-chart-wrap">
|
|
<svg class="sun-constellations-chart" viewBox="0 0 900 270" role="img" aria-label="Zeitlicher Versatz über 100 Jahre in Tagen">
|
|
<?php for ($line = 0; $line <= 4; $line++): ?>
|
|
<?php $value = $chartLow + (($chartHigh - $chartLow) * $line / 4); ?>
|
|
<?php $y = $chartTop + $chartPlotHeight - ($chartPlotHeight * $line / 4); ?>
|
|
<line class="sun-chart-grid" x1="<?= h(number_format($chartLeft, 2, '.', '')) ?>" y1="<?= h(number_format($y, 2, '.', '')) ?>" x2="<?= h(number_format($chartWidth - $chartRight, 2, '.', '')) ?>" y2="<?= h(number_format($y, 2, '.', '')) ?>"></line>
|
|
<text class="sun-chart-axis-label" x="<?= h(number_format($chartLeft - 9, 2, '.', '')) ?>" y="<?= h(number_format($y + 4, 2, '.', '')) ?>" text-anchor="end"><?= h(number_format($value, 1, ',', '.')) ?></text>
|
|
<?php endfor; ?>
|
|
<polyline class="sun-chart-line" points="<?= h(implode(' ', $chartPoints)) ?>"></polyline>
|
|
<polyline class="sun-chart-moving-average" points="<?= h(implode(' ', $movingAveragePoints)) ?>"></polyline>
|
|
<?php foreach ($chartMarkers as $marker): ?>
|
|
<circle class="sun-chart-marker" cx="<?= h(number_format($marker['x'], 2, '.', '')) ?>" cy="<?= h(number_format($marker['y'], 2, '.', '')) ?>" r="3">
|
|
<title><?= h($marker['year'] . ': ' . formatSignedDays($marker['value'])) ?></title>
|
|
</circle>
|
|
<?php endforeach; ?>
|
|
<text class="sun-chart-axis-label" x="<?= h(number_format($chartLeft, 2, '.', '')) ?>" y="<?= h(number_format($chartHeight - 12, 2, '.', '')) ?>" text-anchor="start"><?= h((string) ($yearValues[0]['year'] ?? '')) ?></text>
|
|
<text class="sun-chart-axis-label" x="<?= h(number_format($chartWidth - $chartRight, 2, '.', '')) ?>" y="<?= h(number_format($chartHeight - 12, 2, '.', '')) ?>" text-anchor="end"><?= h((string) ($yearValues[count($yearValues) - 1]['year'] ?? '')) ?></text>
|
|
</svg>
|
|
<p class="hint">Cyan: jährlicher Versatz. Gold: gleitender 10-Jahres-Durchschnitt.</p>
|
|
</div>
|
|
|
|
<details class="sun-constellations-year-details">
|
|
<summary>Jahreswerte anzeigen (<?= h((string) count($yearValues)) ?> Jahre)</summary>
|
|
<div class="sun-constellations-table-wrap">
|
|
<table class="data-table sun-constellations-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Jahr</th>
|
|
<th>Astronomisch</th>
|
|
<th>Astrologisch</th>
|
|
<th>Versatz</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($yearValues as $yearValue): ?>
|
|
<tr>
|
|
<td><?= h((string) ($yearValue['year'] ?? '')) ?></td>
|
|
<td><?= h((string) ($yearValue['astronomical_date'] ?? '')) ?></td>
|
|
<td><?= h((string) ($yearValue['astrological_date'] ?? '')) ?></td>
|
|
<td><?= h(formatSignedDays((float) ($yearValue['difference_days'] ?? 0))) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</details>
|
|
<?php endif; ?>
|
|
</section>
|
|
<?php endif; ?>
|
|
</main>
|
|
|
|
<?php require __DIR__ . '/footer.php'; ?>
|