From a915e3836787cb9a1179fc2572372b97b186daed Mon Sep 17 00:00:00 2001 From: Eskimue Date: Fri, 3 Apr 2026 14:01:26 +0200 Subject: [PATCH] =?UTF-8?q?Mond=20und=20Sonnenn=C3=A4he=20eingebaut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex-temp/php-server.err.log | 39 ++++++ public/monatsvorhersage_old.php | 80 +++++++++++ public/py/__pycache__/api.cpython-314.pyc | Bin 112411 -> 119168 bytes public/py/api.py | 154 +++++++++++++++++++++- 4 files changed, 271 insertions(+), 2 deletions(-) diff --git a/.codex-temp/php-server.err.log b/.codex-temp/php-server.err.log index 7494f4e..cc1a430 100644 --- a/.codex-temp/php-server.err.log +++ b/.codex-temp/php-server.err.log @@ -242,3 +242,42 @@ Stack trace: [Fri Apr 3 13:34:19 2026] 127.0.0.1:63048 Accepted [Fri Apr 3 13:34:20 2026] 127.0.0.1:63048 [200]: GET /favicon.ico [Fri Apr 3 13:34:20 2026] 127.0.0.1:63048 Closing +[Fri Apr 3 13:37:55 2026] 127.0.0.1:52978 Accepted +[Fri Apr 3 13:38:05 2026] 127.0.0.1:52978 [200]: GET /monatsvorhersage_old.php?year=2026&month=8 +[Fri Apr 3 13:38:05 2026] 127.0.0.1:52978 Closing +[Fri Apr 3 13:39:22 2026] 127.0.0.1:58743 Accepted +[Fri Apr 3 13:39:36 2026] 127.0.0.1:58743 [200]: GET /monatsvorhersage_old.php?year=2026&month=3 +[Fri Apr 3 13:39:36 2026] 127.0.0.1:58743 Closing +[Fri Apr 3 13:39:36 2026] 127.0.0.1:54463 Accepted +[Fri Apr 3 13:39:36 2026] 127.0.0.1:54463 [200]: GET /css/style.css +[Fri Apr 3 13:39:36 2026] 127.0.0.1:54463 Closing +[Fri Apr 3 13:39:36 2026] 127.0.0.1:49628 Accepted +[Fri Apr 3 13:39:36 2026] 127.0.0.1:49628 [200]: GET /js/astronomy.browser.min.js +[Fri Apr 3 13:39:36 2026] 127.0.0.1:49628 Closing +[Fri Apr 3 13:39:36 2026] 127.0.0.1:60178 Accepted +[Fri Apr 3 13:39:36 2026] 127.0.0.1:63240 Accepted +[Fri Apr 3 13:39:36 2026] 127.0.0.1:60178 [200]: GET /images/star16x16.png +[Fri Apr 3 13:39:36 2026] 127.0.0.1:63240 [200]: GET /images/star16x16_rays.png +[Fri Apr 3 13:39:36 2026] 127.0.0.1:60178 Closing +[Fri Apr 3 13:39:36 2026] 127.0.0.1:63240 Closing +[Fri Apr 3 13:39:36 2026] 127.0.0.1:53540 Accepted +[Fri Apr 3 13:39:38 2026] 127.0.0.1:53540 [200]: GET /favicon.ico +[Fri Apr 3 13:39:38 2026] 127.0.0.1:53540 Closing +[Fri Apr 3 13:40:20 2026] 127.0.0.1:64903 Accepted +[Fri Apr 3 13:40:28 2026] 127.0.0.1:64903 [200]: GET /monatsvorhersage_old.php?year=2026&month=8 +[Fri Apr 3 13:40:28 2026] 127.0.0.1:64903 Closing +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58859 Accepted +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58859 [200]: GET /css/style.css +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58859 Closing +[Fri Apr 3 13:40:28 2026] 127.0.0.1:63926 Accepted +[Fri Apr 3 13:40:28 2026] 127.0.0.1:63926 [200]: GET /js/astronomy.browser.min.js +[Fri Apr 3 13:40:28 2026] 127.0.0.1:63926 Closing +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58621 Accepted +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58621 [200]: GET /images/star16x16.png +[Fri Apr 3 13:40:28 2026] 127.0.0.1:56978 Accepted +[Fri Apr 3 13:40:28 2026] 127.0.0.1:56978 [200]: GET /images/star16x16_rays.png +[Fri Apr 3 13:40:28 2026] 127.0.0.1:58621 Closing +[Fri Apr 3 13:40:28 2026] 127.0.0.1:56978 Closing +[Fri Apr 3 13:40:28 2026] 127.0.0.1:63117 Accepted +[Fri Apr 3 13:40:30 2026] 127.0.0.1:63117 [200]: GET /favicon.ico +[Fri Apr 3 13:40:30 2026] 127.0.0.1:63117 Closing diff --git a/public/monatsvorhersage_old.php b/public/monatsvorhersage_old.php index b3598e3..e571afd 100644 --- a/public/monatsvorhersage_old.php +++ b/public/monatsvorhersage_old.php @@ -60,6 +60,8 @@ $moonPhaseEvents = []; $moonPlanetApproaches = []; $planetConjunctions = []; $eclipseEvents = []; +$moonApsisEvents = []; +$sunApsisEvents = []; $moonOccultations = []; $seasonChanges = []; $monthEventList = []; @@ -368,6 +370,40 @@ if (is_array($eclipseDecoded) && ($eclipseDecoded['ok'] ?? false)) { $eclipseEvents = is_array($eclipseDecoded['events'] ?? null) ? $eclipseDecoded['events'] : []; } +$moonApsisOutput = []; +$moonApsisExitCode = 0; +$moonApsisArgs = [ + $pythonScriptPath, + 'moon_apsides_for_month', + (string) $selectedYear, + (string) $selectedMonth, + $chartTimezone, +]; +$moonApsisCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $moonApsisArgs)) . ' 2>&1'; +exec($moonApsisCommand, $moonApsisOutput, $moonApsisExitCode); +$moonApsisRaw = trim(implode("\n", $moonApsisOutput)); +$moonApsisDecoded = json_decode($moonApsisRaw, true); +if (is_array($moonApsisDecoded) && ($moonApsisDecoded['ok'] ?? false)) { + $moonApsisEvents = is_array($moonApsisDecoded['events'] ?? null) ? $moonApsisDecoded['events'] : []; +} + +$sunApsisOutput = []; +$sunApsisExitCode = 0; +$sunApsisArgs = [ + $pythonScriptPath, + 'sun_apsides_for_month', + (string) $selectedYear, + (string) $selectedMonth, + $chartTimezone, +]; +$sunApsisCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $sunApsisArgs)) . ' 2>&1'; +exec($sunApsisCommand, $sunApsisOutput, $sunApsisExitCode); +$sunApsisRaw = trim(implode("\n", $sunApsisOutput)); +$sunApsisDecoded = json_decode($sunApsisRaw, true); +if (is_array($sunApsisDecoded) && ($sunApsisDecoded['ok'] ?? false)) { + $sunApsisEvents = is_array($sunApsisDecoded['events'] ?? null) ? $sunApsisDecoded['events'] : []; +} + $moonPlanetOutput = []; $moonPlanetExitCode = 0; $moonPlanetArgs = [ @@ -517,6 +553,50 @@ foreach ($eclipseEvents as $eclipseEvent) { ]; } +foreach ($moonApsisEvents as $moonApsisEvent) { + if (!is_array($moonApsisEvent)) { + continue; + } + + $moonApsisKind = (string) ($moonApsisEvent['kind'] ?? ''); + $distanceText = isset($moonApsisEvent['distance_km']) + ? number_format((float) $moonApsisEvent['distance_km'], 0, ',', '.') . ' km' + : ''; + $moonApsisLabel = $moonApsisKind === 'perigee' + ? 'Mond in Erdnähe (Perigäum)' + : ($moonApsisKind === 'apogee' ? 'Mond in Erdferne (Apogäum)' : (string) ($moonApsisEvent['label'] ?? 'Mondabstand')); + + $monthEventList[] = [ + 'event' => trim($moonApsisLabel . ($distanceText !== '' ? ' (' . $distanceText . ')' : '')), + 'date' => (string) ($moonApsisEvent['local_date'] ?? ''), + 'time' => (string) ($moonApsisEvent['local_time'] ?? ''), + 'local_iso' => (string) ($moonApsisEvent['local_iso'] ?? ''), + 'type' => 'moon_apsis', + ]; +} + +foreach ($sunApsisEvents as $sunApsisEvent) { + if (!is_array($sunApsisEvent)) { + continue; + } + + $sunApsisKind = (string) ($sunApsisEvent['kind'] ?? ''); + $distanceText = isset($sunApsisEvent['distance_km']) + ? number_format((float) $sunApsisEvent['distance_km'], 0, ',', '.') . ' km' + : ''; + $sunApsisLabel = $sunApsisKind === 'perihelion' + ? 'Erde erreicht sonnennächsten Punkt (Perihel)' + : ($sunApsisKind === 'aphelion' ? 'Erde erreicht sonnenfernsten Punkt (Aphel)' : (string) ($sunApsisEvent['label'] ?? 'Sonnenabstand')); + + $monthEventList[] = [ + 'event' => trim($sunApsisLabel . ($distanceText !== '' ? ' (' . $distanceText . ')' : '')), + 'date' => (string) ($sunApsisEvent['local_date'] ?? ''), + 'time' => (string) ($sunApsisEvent['local_time'] ?? ''), + 'local_iso' => (string) ($sunApsisEvent['local_iso'] ?? ''), + 'type' => 'sun_apsis', + ]; +} + foreach ($moonPlanetApproaches as $approachEvent) { if (!is_array($approachEvent)) { continue; diff --git a/public/py/__pycache__/api.cpython-314.pyc b/public/py/__pycache__/api.cpython-314.pyc index 2a0d7ecad968aac84bd2edbe6a6bb6c9fc10056e..b42173168bdc1d352d84b03103735369da82baa4 100644 GIT binary patch delta 7348 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 delta 4646 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 diff --git a/public/py/api.py b/public/py/api.py index d957fbb..aa99651 100644 --- a/public/py/api.py +++ b/public/py/api.py @@ -1014,6 +1014,146 @@ def action_eclipses_for_month(args: list[str]) -> dict: } +def action_moon_apsides_for_month(args: list[str]) -> dict: + if len(args) != 3: + fail( + "Aktion moon_apsides_for_month erwartet 3 Argumente: year month timezone", + extra={"argv": args}, + ) + + try: + year = int(args[0]) + month = int(args[1]) + except ValueError as exc: + fail("Jahr oder Monat sind ungueltig.", extra={"details": str(exc), "argv": args}) + + if month < 1 or month > 12: + fail("Monat muss zwischen 1 und 12 liegen.", extra={"month": month}) + + timezone_name = args[2] + try: + tz = ZoneInfo(timezone_name) + except Exception as exc: + fail("Zeitzone ist ungueltig oder auf dem Server nicht verfuegbar.", extra={"details": str(exc)}) + + local_start = datetime(year, month, 1, 0, 0, 0, tzinfo=tz) + if month == 12: + local_end = datetime(year + 1, 1, 1, 0, 0, 0, tzinfo=tz) + else: + local_end = datetime(year, month + 1, 1, 0, 0, 0, tzinfo=tz) + + utc_start = local_start.astimezone(timezone.utc) + scan_start = dt_to_time(utc_start - timedelta(days=10)) + + events: list[dict] = [] + apsis = astronomy.SearchLunarApsis(scan_start) + while True: + dt_utc = time_to_datetime(apsis.time) + local_dt = dt_utc.astimezone(tz) + if local_dt >= local_end: + break + + if local_dt >= local_start: + is_perigee = apsis.kind == astronomy.ApsisKind.Pericenter + label = "Mondnaehe (Perigaeum)" if is_perigee else "Mondferne (Apogaeum)" + events.append({ + "kind": "perigee" if is_perigee else "apogee", + "label": label, + "distance_km": float(apsis.dist_au * astronomy.KM_PER_AU), + "utc_iso": dt_utc.isoformat().replace("+00:00", "Z"), + "local_iso": local_dt.isoformat(), + "local_date": local_dt.strftime("%d.%m.%Y"), + "local_time": local_dt.strftime("%H:%M"), + }) + + apsis = astronomy.NextLunarApsis(apsis) + + events.sort(key=lambda item: item["local_iso"]) + + return { + "ok": True, + "action": "moon_apsides_for_month", + "selected": { + "year": year, + "month": month, + "timezone": timezone_name, + }, + "events": events, + } + + +def action_sun_apsides_for_month(args: list[str]) -> dict: + if len(args) != 3: + fail( + "Aktion sun_apsides_for_month erwartet 3 Argumente: year month timezone", + extra={"argv": args}, + ) + + try: + year = int(args[0]) + month = int(args[1]) + except ValueError as exc: + fail("Jahr oder Monat sind ungueltig.", extra={"details": str(exc), "argv": args}) + + if month < 1 or month > 12: + fail("Monat muss zwischen 1 und 12 liegen.", extra={"month": month}) + + timezone_name = args[2] + try: + tz = ZoneInfo(timezone_name) + except Exception as exc: + fail("Zeitzone ist ungueltig oder auf dem Server nicht verfuegbar.", extra={"details": str(exc)}) + + local_start = datetime(year, month, 1, 0, 0, 0, tzinfo=tz) + if month == 12: + local_end = datetime(year + 1, 1, 1, 0, 0, 0, tzinfo=tz) + else: + local_end = datetime(year, month + 1, 1, 0, 0, 0, tzinfo=tz) + + utc_start = local_start.astimezone(timezone.utc) + scan_start = dt_to_time(utc_start - timedelta(days=40)) + + events: list[dict] = [] + apsis = astronomy.SearchPlanetApsis(astronomy.Body.Earth, scan_start) + while True: + dt_utc = time_to_datetime(apsis.time) + local_dt = dt_utc.astimezone(tz) + if local_dt >= local_end: + break + + if local_dt >= local_start: + is_perihelion = apsis.kind == astronomy.ApsisKind.Pericenter + label = ( + "Erde erreicht sonnennachsten Punkt (Perihel)" + if is_perihelion + else "Erde erreicht sonnenfernsten Punkt (Aphel)" + ) + events.append({ + "kind": "perihelion" if is_perihelion else "aphelion", + "label": label, + "distance_km": float(apsis.dist_au * astronomy.KM_PER_AU), + "utc_iso": dt_utc.isoformat().replace("+00:00", "Z"), + "local_iso": local_dt.isoformat(), + "local_date": local_dt.strftime("%d.%m.%Y"), + "local_time": local_dt.strftime("%H:%M"), + }) + + apsis = astronomy.NextPlanetApsis(astronomy.Body.Earth, apsis) + + events.sort(key=lambda item: item["local_iso"]) + + return { + "ok": True, + "action": "sun_apsides_for_month", + "selected": { + "year": year, + "month": month, + "timezone": timezone_name, + }, + "events": events, + } + + def action_moon_star_occultations_for_month(args: list[str]) -> dict: if len(args) != 7: fail( @@ -2070,7 +2210,7 @@ def action_satellite_passes(args: list[str]) -> dict: def main() -> None: if len(sys.argv) < 2: - fail("Es wurde keine Aktion uebergeben.", extra={"available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_star_occultations_for_month"]}) + fail("Es wurde keine Aktion uebergeben.", extra={"available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "moon_star_occultations_for_month"]}) action = sys.argv[1] args = sys.argv[2:] @@ -2130,12 +2270,22 @@ def main() -> None: print(json.dumps(result, ensure_ascii=False)) return + if action == "moon_apsides_for_month": + result = action_moon_apsides_for_month(args) + print(json.dumps(result, ensure_ascii=False)) + return + + if action == "sun_apsides_for_month": + result = action_sun_apsides_for_month(args) + print(json.dumps(result, ensure_ascii=False)) + return + if action == "moon_star_occultations_for_month": result = action_moon_star_occultations_for_month(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", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_star_occultations_for_month"]}) + fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "moon_star_occultations_for_month"]}) if __name__ == "__main__":