meteorshower eingebaut

This commit is contained in:
2026-04-03 18:56:02 +02:00
parent 5d5901c623
commit fea8b677f9
2 changed files with 223 additions and 0 deletions
+160
View File
@@ -68,6 +68,7 @@ $venusPeakMagnitudeEvents = [];
$outerPlanetEvents = [];
$outerPlanetStationEvents = [];
$moonOccultations = [];
$meteorShowerPeaks = [];
$seasonChanges = [];
$timeChanges = [];
$monthEventList = [];
@@ -92,6 +93,91 @@ if (!function_exists('buildMonthOccultationStarLabel')) {
}
}
if (!function_exists('normalizeMonthForecastDegree')) {
function normalizeMonthForecastDegree(float $value): float
{
$normalized = fmod($value, 360.0);
if ($normalized < 0.0) {
$normalized += 360.0;
}
return $normalized;
}
}
if (!function_exists('monthForecastSolarLongitude')) {
function monthForecastSolarLongitude(DateTimeImmutable $dateTime): float
{
$julianDay = ((float) $dateTime->format('U')) / 86400.0 + 2440587.5;
$daysSinceJ2000 = $julianDay - 2451545.0;
$meanLongitude = normalizeMonthForecastDegree(280.46 + 0.9856474 * $daysSinceJ2000);
$meanAnomalyRad = deg2rad(normalizeMonthForecastDegree(357.528 + 0.9856003 * $daysSinceJ2000));
return normalizeMonthForecastDegree(
$meanLongitude
+ 1.915 * sin($meanAnomalyRad)
+ 0.020 * sin(2.0 * $meanAnomalyRad)
);
}
}
if (!function_exists('monthForecastForwardDegreeDistance')) {
function monthForecastForwardDegreeDistance(float $from, float $to): float
{
return normalizeMonthForecastDegree($to - $from);
}
}
if (!function_exists('monthForecastRadiantIsVisible')) {
function monthForecastRadiantIsVisible(float $latitudeDeg, ?float $declinationDeg): bool
{
if ($declinationDeg === null) {
return false;
}
return abs($latitudeDeg - $declinationDeg) < 90.0;
}
}
if (!function_exists('findMonthForecastSolarLongitudeCrossing')) {
function findMonthForecastSolarLongitudeCrossing(
DateTimeImmutable $rangeStartLocal,
DateTimeImmutable $rangeEndLocal,
float $targetLongitude,
DateTimeZone $displayTimezone
): ?DateTimeImmutable {
$utc = new DateTimeZone('UTC');
$rangeStartUtc = $rangeStartLocal->setTimezone($utc);
$rangeEndUtc = $rangeEndLocal->setTimezone($utc);
$startLongitude = monthForecastSolarLongitude($rangeStartUtc);
$endLongitude = monthForecastSolarLongitude($rangeEndUtc);
$targetDelta = monthForecastForwardDegreeDistance($startLongitude, $targetLongitude);
$rangeDelta = monthForecastForwardDegreeDistance($startLongitude, $endLongitude);
if ($targetDelta > $rangeDelta + 0.0001) {
return null;
}
$low = (int) $rangeStartUtc->format('U');
$high = (int) $rangeEndUtc->format('U');
for ($i = 0; $i < 40; $i++) {
$mid = (int) floor(($low + $high) / 2);
$midLongitude = monthForecastSolarLongitude(new DateTimeImmutable('@' . $mid));
$midDelta = monthForecastForwardDegreeDistance($startLongitude, $midLongitude);
if ($midDelta < $targetDelta) {
$low = $mid;
} else {
$high = $mid;
}
}
return (new DateTimeImmutable('@' . $high))->setTimezone($displayTimezone);
}
}
try {
$dbConfig = require __DIR__ . '/../config/database.php';
$dsn = sprintf(
@@ -250,6 +336,24 @@ try {
'size' => $row['major_axis'] !== null ? (float) $row['major_axis'] : 8.0,
];
}
$meteorStmt = $pdo->query(
"SELECT designation, shower_code, radiant_delta_deg, peak_sol, zhr, zhr_variable
FROM meteorshowers
WHERE peak_sol IS NOT NULL
ORDER BY peak_sol ASC, designation ASC"
);
while ($row = $meteorStmt->fetch()) {
$meteorShowerPeaks[] = [
'designation' => trim((string) ($row['designation'] ?? '')),
'shower_code' => trim((string) ($row['shower_code'] ?? '')),
'radiant_delta_deg' => $row['radiant_delta_deg'] !== null ? (float) $row['radiant_delta_deg'] : null,
'peak_sol' => (float) $row['peak_sol'],
'zhr' => $row['zhr'] !== null ? (int) $row['zhr'] : null,
'zhr_variable' => trim((string) ($row['zhr_variable'] ?? '')),
];
}
} catch (Throwable $e) {
$starDataError = $e->getMessage();
}
@@ -884,6 +988,62 @@ foreach ($moonOccultations as $occultationEvent) {
];
}
$monthStartLocal = new DateTimeImmutable(
sprintf('%04d-%02d-01 00:00:00', $selectedYear, $selectedMonth),
new DateTimeZone($chartTimezone)
);
$monthEndLocal = $monthStartLocal->modify('first day of next month');
foreach ($meteorShowerPeaks as $meteorShowerPeak) {
if (!monthForecastRadiantIsVisible($chartLatitude, $meteorShowerPeak['radiant_delta_deg'] ?? null)) {
continue;
}
$peakSol = isset($meteorShowerPeak['peak_sol']) ? (float) $meteorShowerPeak['peak_sol'] : null;
if ($peakSol === null) {
continue;
}
$peakDateTime = findMonthForecastSolarLongitudeCrossing(
$monthStartLocal,
$monthEndLocal,
$peakSol,
new DateTimeZone($chartTimezone)
);
if (!$peakDateTime instanceof DateTimeImmutable) {
continue;
}
$designation = trim((string) ($meteorShowerPeak['designation'] ?? ''));
$showerCode = trim((string) ($meteorShowerPeak['shower_code'] ?? ''));
$zhr = $meteorShowerPeak['zhr'] ?? null;
$zhrVariable = trim((string) ($meteorShowerPeak['zhr_variable'] ?? ''));
$rateText = '';
if (is_int($zhr) && $zhr >= 0) {
$rateText = 'ZHR ca. ' . $zhr;
} elseif ($zhrVariable !== '') {
$rateText = 'ZHR ca. ' . $zhrVariable;
}
$labelParts = [];
$labelParts[] = 'Maximum ' . ($designation !== '' ? $designation : 'Meteorstrom');
if ($showerCode !== '') {
$labelParts[] = $showerCode;
}
if ($rateText !== '') {
$labelParts[] = $rateText;
}
$monthEventList[] = [
'event' => implode(' - ', $labelParts),
'date' => $peakDateTime->format('d.m.Y'),
'time' => $peakDateTime->format('H:i'),
'local_iso' => $peakDateTime->format(DateTimeInterface::ATOM),
'type' => 'meteor_shower_peak',
];
}
usort(
$monthEventList,
static fn (array $a, array $b): int => strcmp((string) ($a['local_iso'] ?? ''), (string) ($b['local_iso'] ?? ''))