Neue Seite fuer Mond-Planeten-Bedeckungen mit Jahresberechnung hinzufuegen

Berechnet Bedeckungen der Planeten Merkur bis Neptun durch den Mond fuer
ein ganzes Jahr am Standardstandort, inklusive Detailkarte. Ereignisse
unter dem Horizont werden weiterhin angezeigt statt gefiltert, dafuer
mit eigenem Sichtbarkeits-Hinweis.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 09:35:36 +02:00
co-authored by Claude Sonnet 5
parent 86fccdc773
commit 5652cc7e40
6 changed files with 1448 additions and 2 deletions
+1
View File
@@ -49,6 +49,7 @@ Nicht jede Berechnung ist Skyfield oder Astronomy Engine:
| `sonnenfinsternis.php` | **Astronomy Engine**, einschließlich lokaler Berechnung für den Standardstandort. |
| `mondfinsternis.php` | **Astronomy Engine**. Der Zeitpunkt der Finsternis ist global; eine lokale Sichtbarkeitsbewertung ist davon getrennt. |
| `sternbedeckungen.php` | **Astronomy Engine** über die lokale Python-API. Topozentrische Koordinaten und der Standardstandort werden berücksichtigt. |
| `mondplanetenbedeckungen.php` | **Astronomy Engine** über die lokale Python-API. Durchsucht ein ganzes Jahr nach Mond-Planeten-Bedeckungen (Merkur bis Neptun) für den Standardstandort; berücksichtigt die scheinbare Scheibengröße von Mond und Planet. |
| `moonphase.php` | **Astronomy Engine** im Browser beziehungsweise über die lokale Python-API. |
| `moonyear.php` | **Astronomy Engine** für Mondphasen und Mondpositionen. |
| `satellitenhimmel.php` | **SGP4** für Satellitenbahnen; Astronomy Engine für astronomischen Kontext wie Sonne, Mond und Dämmerung. |
+1
View File
@@ -134,6 +134,7 @@ function manageablePublicPages(): array
'sonnenfinsternis.php' => 'Sonnenfinsternis',
'bedeckung.php' => 'Bedeckungsphänomene',
'sternbedeckungen.php' => 'Mond-Stern-Bedeckungen',
'mondplanetenbedeckungen.php' => 'Mond-Planeten-Bedeckungen',
'monatsvorhersage.php' => 'Monatsvorhersage',
'moonyear.php' => 'Mondphasen-Jahresübersicht',
'wochenvorhersage.php' => 'Wochenvorhersage',
+6
View File
@@ -4676,6 +4676,12 @@ body.solarsystem-page:not(.jupitersystem-page) .solarsystem-stage-wrap {
box-shadow: inset 0 0 0 1px rgba(143, 230, 141, 0.22);
}
.occult-visibility-below {
background: rgba(136, 148, 173, 0.16);
color: #9aa6c2;
box-shadow: inset 0 0 0 1px rgba(154, 166, 194, 0.22);
}
.occult-detail-card {
overflow: hidden;
}
+1
View File
@@ -146,6 +146,7 @@ $menuGroups = [
['href' => 'sonnenfinsternis.php', 'label' => 'Sonnenfinsternis'],
['href' => 'bedeckung.php', 'label' => 'Bedeckungsphänomene'],
['href' => 'sternbedeckungen.php', 'label' => 'Mond-Stern-Bedeckungen'],
['href' => 'mondplanetenbedeckungen.php', 'label' => 'Mond-Planeten-Bedeckungen'],
],
],
[
File diff suppressed because it is too large Load Diff
+155 -2
View File
@@ -5844,6 +5844,154 @@ def action_moon_star_occultations(args: list[str]) -> dict:
}
PLANET_OCCULTATION_DEFS = [
("Merkur", astronomy.Body.Mercury, "mercury"),
("Venus", astronomy.Body.Venus, "venus"),
("Mars", astronomy.Body.Mars, "mars"),
("Jupiter", astronomy.Body.Jupiter, "jupiter"),
("Saturn", astronomy.Body.Saturn, "saturn"),
("Uranus", astronomy.Body.Uranus, "uranus"),
("Neptun", astronomy.Body.Neptune, "neptune"),
]
def compute_moon_planet_occultations_for_range(
observer: astronomy.Observer,
utc_start: datetime,
utc_end: datetime,
tz: ZoneInfo,
) -> list[dict]:
scan_step = timedelta(hours=1)
coarse_threshold_deg = 3.0
results: list[dict] = []
for label, body, key in PLANET_OCCULTATION_DEFS:
samples: list[tuple[datetime, float]] = []
current = utc_start
while current <= utc_end:
samples.append((current, moon_planet_separation_deg(body, observer, current)))
current += scan_step
if samples[-1][0] < utc_end:
samples.append((utc_end, moon_planet_separation_deg(body, observer, utc_end)))
seen_ranges: list[tuple[datetime, datetime]] = []
for index in range(1, len(samples) - 1):
prev_t, prev_sep = samples[index - 1]
curr_t, curr_sep = samples[index]
next_t, next_sep = samples[index + 1]
if curr_sep > coarse_threshold_deg:
continue
if curr_sep > prev_sep or curr_sep > next_sep:
continue
left = max(utc_start, curr_t - scan_step)
right = min(utc_end, curr_t + scan_step)
if any(not (right <= seen_left or left >= seen_right) for seen_left, seen_right in seen_ranges):
continue
min_time_utc, min_sep = refine_minimum_separation(body, observer, left, right)
seen_ranges.append((left, right))
overlap_fraction = moon_planet_overlap_fraction(body, observer, min_time_utc)
if overlap_fraction <= 0.0:
continue
time_value = dt_to_time(min_time_utc)
moon_eq = astronomy.Equator(astronomy.Body.Moon, time_value, observer, True, True)
planet_eq = astronomy.Equator(body, time_value, observer, True, True)
sun_eq = astronomy.Equator(astronomy.Body.Sun, time_value, observer, True, True)
moon_hor = astronomy.Horizon(time_value, observer, moon_eq.ra, moon_eq.dec, astronomy.Refraction.Normal)
planet_hor = astronomy.Horizon(time_value, observer, planet_eq.ra, planet_eq.dec, astronomy.Refraction.Normal)
sun_hor = astronomy.Horizon(time_value, observer, sun_eq.ra, sun_eq.dec, astronomy.Refraction.Normal)
below_horizon = float(moon_hor.altitude) <= 0.0 or float(planet_hor.altitude) <= 0.0
ingress_time = refine_occultation_contact(body, observer, min_time_utc, -1)
egress_time = refine_occultation_contact(body, observer, min_time_utc, 1)
duration_seconds = None
if ingress_time is not None and egress_time is not None:
duration_seconds = int(round((egress_time - ingress_time).total_seconds()))
moon_radius_deg = moon_angular_radius_deg(float(moon_eq.dist))
planet_radius_deg = planet_angular_radius_deg(body, float(planet_eq.dist))
results.append({
"planet_key": key,
"planet_label": label,
"ingress": serialize_occultation_time(ingress_time, tz),
"maximum": serialize_occultation_time(min_time_utc, tz),
"egress": serialize_occultation_time(egress_time, tz),
"partial_start": ingress_time is None,
"partial_end": egress_time is None,
"duration_seconds": duration_seconds,
"separation_arcmin": float(min_sep) * 60.0,
"moon_radius_arcmin": moon_radius_deg * 60.0,
"planet_radius_arcsec": planet_radius_deg * 3600.0,
"overlap_fraction": float(overlap_fraction),
"is_total": overlap_fraction >= 0.999,
"moon_alt_deg": float(moon_hor.altitude),
"planet_alt_deg": float(planet_hor.altitude),
"sun_alt_deg": float(sun_hor.altitude),
"below_horizon": below_horizon,
"visibility": {"key": "below_horizon", "label": "Unter Horizont"} if below_horizon
else classify_visibility(float(sun_hor.altitude)),
})
results.sort(
key=lambda item: item["maximum"]["utc_iso"] if item["maximum"] else "9999-99-99T99:99:99Z"
)
return results
def action_moon_planet_occultations_for_year(args: list[str]) -> dict:
if len(args) != 5:
fail(
"Aktion moon_planet_occultations_for_year erwartet 5 Argumente: latitude longitude elevation year timezone",
extra={"argv": args},
)
latitude = parse_float(args[0], "Latitude")
longitude = parse_float(args[1], "Longitude")
elevation = parse_float(args[2], "Elevation")
try:
year = int(args[3])
except ValueError as exc:
fail("Jahr ist ungueltig.", extra={"details": str(exc), "argv": args})
timezone_name = args[4]
try:
tz = ZoneInfo(timezone_name)
except Exception as exc:
fail("Zeitzone ist ungueltig oder auf dem Server nicht verfuegbar.", extra={"details": str(exc)})
observer = astronomy.Observer(latitude, longitude, elevation)
local_start = datetime(year, 1, 1, 0, 0, 0, tzinfo=tz)
local_end = datetime(year + 1, 1, 1, 0, 0, 0, tzinfo=tz)
utc_start = local_start.astimezone(timezone.utc)
utc_end = local_end.astimezone(timezone.utc)
results = compute_moon_planet_occultations_for_range(observer, utc_start, utc_end, tz)
return {
"ok": True,
"action": "moon_planet_occultations_for_year",
"selected": {
"year": year,
"timezone": timezone_name,
},
"observer": {
"latitude": latitude,
"longitude": longitude,
"elevation": elevation,
},
"results": results,
}
# ── satellite_passes ──────────────────────────────────────────────────────────
import math as _math
@@ -6415,7 +6563,7 @@ def action_barycenter(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", "solar_longitude_to_datetime", "current_solar_longitude", "astronomical_conversions", "comet_brightnesses", "favorite_comet_events_for_month", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "planet_ephemeris", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "golden_gate_of_ecliptic_for_month", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "planet_parades_for_month", "planet_constellation_changes_for_month", "sun_constellation_changes_for_year", "sun_constellation_offset_statistics", "moon_star_occultations_for_month"]})
fail("Es wurde keine Aktion uebergeben.", extra={"available_actions": ["sun_moon_rise_set", "solar_longitude_to_datetime", "current_solar_longitude", "astronomical_conversions", "comet_brightnesses", "favorite_comet_events_for_month", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "planet_ephemeris", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "golden_gate_of_ecliptic_for_month", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "planet_parades_for_month", "planet_constellation_changes_for_month", "sun_constellation_changes_for_year", "sun_constellation_offset_statistics", "moon_star_occultations_for_month", "moon_planet_occultations_for_year"]})
action = sys.argv[1]
args = sys.argv[2:]
@@ -6605,6 +6753,11 @@ def main() -> None:
print(json.dumps(result, ensure_ascii=True))
return
if action == "moon_planet_occultations_for_year":
result = action_moon_planet_occultations_for_year(args)
print(json.dumps(result, ensure_ascii=True))
return
if action == "lunar_eclipses_for_year":
result = action_lunar_eclipses_for_year(args)
print(json.dumps(result, ensure_ascii=True))
@@ -6615,7 +6768,7 @@ def main() -> None:
print(json.dumps(result, ensure_ascii=True))
return
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "solar_longitude_to_datetime", "current_solar_longitude", "astronomical_conversions", "twilight_chart", "comet_brightnesses", "favorite_comet_events_for_month", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "planet_ephemeris", "planet_visibility_chart", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "golden_gate_of_ecliptic_for_month", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "planet_parades_for_month", "planet_constellation_changes_for_month", "sun_constellation_changes_for_year", "sun_constellation_offset_statistics", "moon_star_occultations_for_month"]})
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "solar_longitude_to_datetime", "current_solar_longitude", "astronomical_conversions", "twilight_chart", "comet_brightnesses", "favorite_comet_events_for_month", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "planet_ephemeris", "planet_visibility_chart", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "golden_gate_of_ecliptic_for_month", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "planet_parades_for_month", "planet_constellation_changes_for_month", "sun_constellation_changes_for_year", "sun_constellation_offset_statistics", "moon_star_occultations_for_month", "moon_planet_occultations_for_year"]})
if __name__ == "__main__":