diff --git a/public/monatsvorhersage.php b/public/monatsvorhersage.php
index 37082fe..57d24cc 100644
--- a/public/monatsvorhersage.php
+++ b/public/monatsvorhersage.php
@@ -14,6 +14,7 @@ $selectedMonth = isset($_GET['month']) ? max(1, min(12, (int) $_GET['month'])) :
$selectedWeek = isset($_GET['week']) ? max(1, min(53, (int) $_GET['week'])) : (int) date('W');
$selectedWeekYear = isset($_GET['week_year']) ? (int) $_GET['week_year'] : $currentYear;
$shouldRunForecast = isset($_GET['run']) && $_GET['run'] === '1';
+$includeComets = isset($_GET['include_comets']) && $_GET['include_comets'] === '1';
if (!isset($_SESSION['observation_tip_insert_token']) || !is_string($_SESSION['observation_tip_insert_token'])) {
$_SESSION['observation_tip_insert_token'] = bin2hex(random_bytes(16));
}
@@ -45,10 +46,10 @@ $observationTipInsertToken = $_SESSION['observation_tip_insert_token'];
// - Zusammengeführte Monatsereignisliste für Übersicht, SQL-Export und iCal
// -------------------------------------------------------------------------
-// Grenzwerte fuer Sternkarte und Objektanzeige
+// Grenzwerte für Sternkarte und Objektanzeige
// -------------------------------------------------------------------------
// Maximale Helligkeit (Magnitude) der Sterne, die aus der Datenbank geladen
-// werden. Hoehere Werte laden mehr, aber auch schwaechere Sterne.
+// werden. Höhere Werte laden mehr, aber auch schwächere Sterne.
$starDatabaseMagnitudeLimit = 6.0;
// Maximale Helligkeit der Sterne, die auf der Monats-Sternkarte sichtbar
@@ -67,95 +68,95 @@ $messierDatabaseMagnitudeLimit = 2.0;
$messierDisplayMagnitudeLimit = 1.0;
// -------------------------------------------------------------------------
-// Grenzwerte fuer Monatsereignisse
+// Grenzwerte für Monatsereignisse
// -------------------------------------------------------------------------
-// Maximaler Winkelabstand fuer Begegnungen zwischen Mond und Planeten.
+// Maximaler Winkelabstand für Begegnungen zwischen Mond und Planeten.
$moonPlanetMaxSeparationDeg = 1.5;
-// Maximaler Winkelabstand fuer Begegnungen zwischen Mond und ausgewaehlten
+// Maximaler Winkelabstand für Begegnungen zwischen Mond und ausgewählten
// Sternhaufen wie Plejaden oder Praesepe.
$moonDeepSkyMaxSeparationDeg = 1.0;
-// Maximaler Winkelabstand fuer Begegnungen zwischen hellen Planeten und
-// ausgewaehlten hellen Sternen wie Regulus oder Spica.
+// Maximaler Winkelabstand für Begegnungen zwischen hellen Planeten und
+// ausgewählten hellen Sternen wie Regulus oder Spica.
$planetBrightStarMaxSeparationDeg = 1.0;
-// Maximaler Winkelabstand fuer Planet-Planet-Konjunktionen.
+// Maximaler Winkelabstand für Planet-Planet-Konjunktionen.
$planetConjunctionMaxSeparationDeg = 1.0;
-// Nur Sterne bis zu dieser Helligkeit werden fuer Mondbedeckungen
-// beruecksichtigt.
+// Nur Sterne bis zu dieser Helligkeit werden für Mondbedeckungen
+// berücksichtigt.
$moonOccultationMaxMag = 2.0;
// -------------------------------------------------------------------------
-// Schwellen fuer "Gute Satelliten-Ueberfluege"
+// Schwellen für "Gute Satelliten-Überflüge"
// -------------------------------------------------------------------------
-// Es wird nur der Favoriten-Satellit des angemeldeten Users geprueft.
+// Es wird nur der Favoriten-Satellit des angemeldeten Users geprüft.
//
-// Ein Ueberflug zaehlt als guter Beobachtungstermin nur dann, wenn er am
-// Hoehepunkt mindestens diese Hoehe ueber dem Horizont erreicht.
+// Ein Überflug zählt als guter Beobachtungstermin nur dann, wenn er am
+// Höhepunkt mindestens diese Höhe über dem Horizont erreicht.
$favoriteSatelliteBestPassMinAltitudeDeg = 80.0;
-// Gleichzeitig muss die Sonne zum Maximum des Ueberflugs mindestens so weit
+// Gleichzeitig muss die Sonne zum Maximum des Überflugs mindestens so weit
// unter dem Horizont stehen. -12 Grad entspricht dem Beginn/Ende der
-// nautischen Daemmerung und sorgt fuer ausreichend dunklen Himmel.
+// nautischen Dämmerung und sorgt für ausreichend dunklen Himmel.
$favoriteSatelliteBestPassMaxSunAltitudeDeg = -14.0;
-// Optional koennen wir die Liste auf die erste Nachthaelfte begrenzen.
-// "Erste Nachthaelfte" ist hier bewusst pragmatisch als lokaler Abend vor
-// Mitternacht definiert. Ueberfluege nach 00:00 Ortszeit werden damit
+// Optional können wir die Liste auf die erste Nachthälfte begrenzen.
+// "Erste Nachthälfte" ist hier bewusst pragmatisch als lokaler Abend vor
+// Mitternacht definiert. Überflüge nach 00:00 Ortszeit werden damit
// ausgefiltert.
$favoriteSatelliteBestPassOnlyFirstNightHalf = true;
-// Optional koennen wir nur besonders helle Ueberfluege anzeigen.
+// Optional können wir nur besonders helle Überflüge anzeigen.
// Bei Magnituden gilt: kleinere Werte sind heller. 0.0 mag bedeutet also,
-// dass nur Ueberfluege mit Peak-Helligkeit von 0 mag oder heller bleiben.
+// dass nur Überflüge mit Peak-Helligkeit von 0 mag oder heller bleiben.
$favoriteSatelliteBestPassMaxPeakMagnitude = 0.0;
// -------------------------------------------------------------------------
-// Schwellen fuer "Gute Merkur-Sichtbarkeit"
+// Schwellen für "Gute Merkur-Sichtbarkeit"
// -------------------------------------------------------------------------
-// Gute Merkur-Sichtbarkeit wird nicht nur ueber Elongation definiert, sondern
-// ueber echte Beobachtbarkeit in der Daemmerung.
-// Geprueft wird pro Tag:
+// Gute Merkur-Sichtbarkeit wird nicht nur über Elongation definiert, sondern
+// über echte Beobachtbarkeit in der Dämmerung.
+// Geprüft wird pro Tag:
// morgens eine feste Zahl Minuten VOR Sonnenaufgang,
// abends eine feste Zahl Minuten NACH Sonnenuntergang.
$mercuryGoodVisibilitySampleOffsetMinutes = 45;
-// Merkur muss zum Pruefzeitpunkt mindestens diese Hoehe ueber dem Horizont
+// Merkur muss zum Prüfzeitpunkt mindestens diese Höhe über dem Horizont
// erreichen, damit der Tag als gut sichtbar gilt.
$mercuryGoodVisibilityMinAltitudeDeg = 7.0;
// Gleichzeitig muss die Sonne ausreichend tief unter dem Horizont stehen.
-// So vermeiden wir Treffer, bei denen Merkur zwar formal sichtbar waere, aber
-// noch zu stark in der hellen Daemmerung untergeht.
+// So vermeiden wir Treffer, bei denen Merkur zwar formal sichtbar wäre, aber
+// noch zu stark in der hellen Dämmerung untergeht.
$mercuryGoodVisibilityMaxSunAltitudeDeg = -3.0;
// Erst ab dieser Anzahl aufeinanderfolgender guter Tage erzeugen wir ein
-// Monatsereignis. Einzelne Ausreisser-Tage werden dadurch herausgefiltert.
+// Monatsereignis. Einzelne Ausreißer-Tage werden dadurch herausgefiltert.
$mercuryGoodVisibilityMinConsecutiveDays = 3;
// -------------------------------------------------------------------------
-// Schwellen fuer "Planetenparade"
+// Schwellen für "Planetenparade"
// -------------------------------------------------------------------------
-// Geprueft wird morgens eine feste Zahl Minuten VOR Sonnenaufgang und abends
+// Geprüft wird morgens eine feste Zahl Minuten VOR Sonnenaufgang und abends
// eine feste Zahl Minuten NACH Sonnenuntergang.
$planetParadeSampleOffsetMinutes = 45;
-// Ein Planet zaehlt fuer die Parade nur dann mit, wenn er zum Pruefzeitpunkt
-// mindestens diese Hoehe ueber dem Horizont erreicht.
+// Ein Planet zählt für die Parade nur dann mit, wenn er zum Prüfzeitpunkt
+// mindestens diese Höhe über dem Horizont erreicht.
$planetParadeMinAltitudeDeg = 7.0;
// Gleichzeitig muss die Sonne bereits tief genug unter dem Horizont stehen,
// damit die Parade nicht nur rechnerisch, sondern auch praktisch sichtbar ist.
$planetParadeMaxSunAltitudeDeg = -4.0;
-// Die beteiligten Planeten muessen innerhalb dieses Azimutbogens liegen.
-// So vermeiden wir Treffer, bei denen die Planeten zu weit ueber den Himmel
-// verteilt sind und nicht mehr als zusammenhaengende Parade wirken.
+// Die beteiligten Planeten müssen innerhalb dieses Azimutbogens liegen.
+// So vermeiden wir Treffer, bei denen die Planeten zu weit über den Himmel
+// verteilt sind und nicht mehr als zusammenhängende Parade wirken.
$planetParadeMaxAzimuthSpanDeg = 120.0;
-// Mindestens so viele helle Planeten muessen gleichzeitig sichtbar sein,
+// Mindestens so viele helle Planeten müssen gleichzeitig sichtbar sein,
// damit wir von einer Planetenparade sprechen.
$planetParadeMinPlanetCount = 4;
@@ -164,10 +165,10 @@ $planetParadeMinPlanetCount = 4;
$planetParadeMinConsecutiveDays = 2;
// -------------------------------------------------------------------------
-// Schwellen fuer ausgewaehlte Kometen
+// Schwellen für ausgewählte Kometen
// -------------------------------------------------------------------------
// Gute Kometensichtbarkeit wird morgens vor Sonnenaufgang und abends nach
-// Sonnenuntergang geprueft.
+// Sonnenuntergang geprüft.
$favoriteCometVisibilitySampleOffsetMinutes = 60;
// Der Komet sollte ausreichend hoch stehen, damit die Beobachtung praktisch
@@ -177,7 +178,7 @@ $favoriteCometVisibilityMinAltitudeDeg = 10.0;
// Die Sonne muss tief genug unter dem Horizont stehen.
$favoriteCometVisibilityMaxSunAltitudeDeg = -8.0;
-// Optional koennen wir eine grobe Helligkeitsschwelle setzen, damit nur
+// Optional können wir eine grobe Helligkeitsschwelle setzen, damit nur
// realistisch interessante Fenster erscheinen.
$favoriteCometVisibilityMaxMagnitude = 10.0;
@@ -273,6 +274,7 @@ $planetDataJson = '[]';
$moonDataJson = 'null';
$monthChartCometsJson = '[]';
$moonPhaseEventsJson = '[]';
+$monthEventListJson = '[]';
$starDataErrorJson = 'null';
$observationTipCalendarIcsJson = '""';
$observationTipCalendarFilenameJson = json_encode(
@@ -1023,7 +1025,7 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
}
if ($moonSentences !== []) {
- $paragraphs[] = implode(' ', $moonSentences);
+ $paragraphs[] = implode("\n", $moonSentences);
}
}
@@ -1311,42 +1313,45 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
$paragraphs[] = $eclipseIntro . ' In diesem Monat stechen ' . $describeEvents($eclipseEvents) . ' hervor.';
}
- $meteorEvents = $findEvents(
+ $meteorCandidates = array_values(array_filter(
$filteredEvents,
static fn (array $event): bool => (string) ($event['type'] ?? '') === 'meteor_shower_peak'
- && (
- (int) ($event['zhr_min_estimate'] ?? 0) >= 20
- || (int) ($event['zhr_max_estimate'] ?? 0) >= 30
- ),
- 2
- );
+ && max(
+ (int) ($event['zhr_min_estimate'] ?? 0),
+ (int) ($event['zhr_max_estimate'] ?? 0)
+ ) >= 10
+ ));
+ usort($meteorCandidates, static function (array $left, array $right): int {
+ $leftRate = max((int) ($left['zhr_min_estimate'] ?? 0), (int) ($left['zhr_max_estimate'] ?? 0));
+ $rightRate = max((int) ($right['zhr_min_estimate'] ?? 0), (int) ($right['zhr_max_estimate'] ?? 0));
+ return $rightRate <=> $leftRate;
+ });
+
+ // Der stärkste Strom wird immer genannt. Ist er kein bekannter Jahresstrom,
+ // ergänzen wir einen solchen, damit etwa die Leoniden nicht untergehen.
+ $meteorEvents = $meteorCandidates === [] ? [] : [$meteorCandidates[0]];
+ $establishedMeteorCodes = ['QUA', 'LYR', 'ETA', 'SDA', 'PER', 'DRA', 'ORI', 'NTA', 'STA', 'LEO', 'GEM', 'URS'];
+ $strongestCode = $meteorEvents === []
+ ? ''
+ : strtoupper(trim((string) ($meteorEvents[0]['meteor_shower_code'] ?? '')));
+ if ($meteorEvents !== [] && !in_array($strongestCode, $establishedMeteorCodes, true)) {
+ foreach (array_slice($meteorCandidates, 1) as $meteorCandidate) {
+ $candidateCode = strtoupper(trim((string) ($meteorCandidate['meteor_shower_code'] ?? '')));
+ if (in_array($candidateCode, $establishedMeteorCodes, true)) {
+ $meteorEvents[] = $meteorCandidate;
+ break;
+ }
+ }
+ }
+
if ($meteorEvents !== []) {
$meteorIntro = $pickRandomText([
'Freunde von Meteoren sollten zudem die Aktivität der Meteorströme im Blick behalten.',
'Auch die Meteorströme verdienen in diesem Monat Aufmerksamkeit.',
'Wer nach Sternschnuppen Ausschau halten möchte, sollte auch die Meteorströme beachten.',
- 'Ein Blick auf die Meteorstroeme kann sich in diesem Monat ebenfalls lohnen.',
- ], 'Freunde von Meteoren sollten zudem die Aktivitaet der Meteorstroeme im Blick behalten.');
+ 'Ein Blick auf die Meteorströme kann sich in diesem Monat ebenfalls lohnen.',
+ ], 'Freunde von Meteoren sollten zudem die Aktivität der Meteorströme im Blick behalten.');
$paragraphs[] = $meteorIntro . ' Erwähnenswert sind besonders ' . $describeEvents($meteorEvents) . '.';
- } else {
- $meteorEvents = $findEvents(
- $filteredEvents,
- static fn (array $event): bool => (string) ($event['type'] ?? '') === 'meteor_shower_peak'
- && (
- (int) ($event['zhr_min_estimate'] ?? 0) >= 10
- || (int) ($event['zhr_max_estimate'] ?? 0) >= 20
- ),
- 2
- );
- if ($meteorEvents !== []) {
- $meteorModerateIntro = $pickRandomText([
- 'Bei den Meteorströmen lohnt in diesem Monat vor allem ein Blick auf',
- 'Unter den Meteorströmen verdienen in diesem Monat besonders',
- 'Auch bei den Meteorströmen fallen in diesem Monat vor allem',
- 'Wer Meteorströme verfolgen möchte, sollte in diesem Monat vor allem auf',
- ], 'Bei den Meteorstroemen lohnt in diesem Monat vor allem ein Blick auf');
- $paragraphs[] = $meteorModerateIntro . ' ' . $describeEvents($meteorEvents) . '.';
- }
}
if (count($paragraphs) === 1 && $filteredEvents !== []) {
@@ -1719,7 +1724,7 @@ if (!empty($favoriteSatellitePayload)) {
}
}
-if (!empty($favoriteCometPayload)) {
+if ($includeComets && !empty($favoriteCometPayload)) {
$tempFavoriteCometFile = tempnam(sys_get_temp_dir(), 'month_comets_');
if ($tempFavoriteCometFile !== false) {
try {
@@ -2679,6 +2684,7 @@ foreach ($meteorShowerPeaks as $meteorShowerPeak) {
'local_iso' => $peakDateTime->format(DateTimeInterface::ATOM),
'zhr_min_estimate' => $zhrMinEstimate,
'zhr_max_estimate' => $zhrMaxEstimate,
+ 'meteor_shower_code' => $showerCode,
'type' => 'meteor_shower_peak',
];
@@ -2743,6 +2749,7 @@ usort(
);
$monthNarrativeText = monthForecastBuildNarrativeText($monthEventList, $selectedMonth, $months[$selectedMonth], $selectedYear);
+$monthEventListJson = json_encode($monthEventList, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '[]';
$observationTipInsertSql = buildObservationTipInsertSql($monthEventList, new DateTimeZone($chartTimezone));
$observationTipCalendarIcs = buildObservationTipCalendarIcs($monthEventList, new DateTimeZone($chartTimezone));
@@ -2795,7 +2802,7 @@ $monthChartComets = array_map(
'G' => array_key_exists('slope_parameter_g', $comet) && $comet['slope_parameter_g'] !== null ? (float) $comet['slope_parameter_g'] : null,
];
},
- $favoriteCometPayload
+ $includeComets ? $favoriteCometPayload : []
);
$monthChartCometsJson = json_encode($monthChartComets, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$moonPhaseEventsJson = json_encode($moonPhaseEvents, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -2843,6 +2850,11 @@ $observationTipCalendarFilenameJson = json_encode(sprintf('astrotools-monatserei
+
+