Mondereignisse vollständig und kompakt ausgeben
This commit is contained in:
@@ -913,13 +913,16 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
|
||||
));
|
||||
|
||||
$phaseEvents = array_values(array_filter($sortedMoonEvents, static fn (array $e): bool => (string) ($e['type'] ?? '') === 'moon_phase'));
|
||||
// Alle Mondphasen müssen erhalten bleiben. Die übrigen Mondereignisse
|
||||
// werden weiterhin begrenzt, damit der Übersichtstext kompakt bleibt.
|
||||
$nonPhaseEvents = array_slice(
|
||||
array_values(array_filter($sortedMoonEvents, static fn (array $e): bool => (string) ($e['type'] ?? '') !== 'moon_phase')),
|
||||
0,
|
||||
10
|
||||
);
|
||||
// Alle Mondphasen und alle Mondereignisse werden vollständig übernommen.
|
||||
$nonPhaseCandidates = array_values(array_filter(
|
||||
$sortedMoonEvents,
|
||||
static fn (array $e): bool => (string) ($e['type'] ?? '') !== 'moon_phase'
|
||||
));
|
||||
$nonPhaseEvents = $nonPhaseCandidates;
|
||||
usort($nonPhaseEvents, static fn (array $a, array $b): int => strcmp(
|
||||
(string) ($a['local_iso'] ?? $a['date'] ?? ''),
|
||||
(string) ($b['local_iso'] ?? $b['date'] ?? '')
|
||||
));
|
||||
|
||||
$moonSentences = [];
|
||||
|
||||
@@ -985,7 +988,7 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
|
||||
}
|
||||
|
||||
if ($goldenHandle !== null) {
|
||||
$moonSentences[] = 'Am ' . $sd . ' zeigt der Mond den Goldenen Henkel: Ein Gebirgskamm am Mondrand leuchtet bereits im Sonnenlicht, während das Tal dahinter noch im Schatten liegt – ein lohnender Anblick schon im kleinen Teleskop.';
|
||||
$moonSentences[] = 'Am ' . $sd . ' zeigt der Mond den Goldenen Henkel.';
|
||||
}
|
||||
|
||||
if ($apsis !== null) {
|
||||
@@ -1057,6 +1060,9 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
|
||||
if (str_starts_with($apLabel, 'Mond nahe ')) {
|
||||
$target = substr($apLabel, strlen('Mond nahe '));
|
||||
$moonSentences[] = 'Am ' . $apSd . ' zieht der Mond' . $sepStr . ' an ' . $target . ' vorbei.';
|
||||
} elseif (str_starts_with($apLabel, 'Mond streift ')) {
|
||||
$target = substr($apLabel, strlen('Mond streift '));
|
||||
$moonSentences[] = 'Am ' . $apSd . ' streift der Mond ' . $target . '.';
|
||||
} elseif (str_starts_with($apLabel, 'Mond bedeckt ')) {
|
||||
$target = substr($apLabel, strlen('Mond bedeckt '));
|
||||
$moonSentences[] = 'Am ' . $apSd . ' bedeckt der Mond ' . $target . '.';
|
||||
|
||||
+87
-9
@@ -2917,15 +2917,28 @@ def moon_planet_occultation_margin_deg(
|
||||
moon_eq = astronomy.Equator(astronomy.Body.Moon, time_value, observer, True, True)
|
||||
body_eq = astronomy.Equator(body, time_value, observer, True, True)
|
||||
moon_radius = moon_angular_radius_deg(float(moon_eq.dist))
|
||||
planet_radius_km = PLANET_RADIUS_KM[body]
|
||||
planet_ratio = planet_radius_km / (float(body_eq.dist) * astronomy.KM_PER_AU)
|
||||
planet_radius = math.degrees(math.asin(max(-1.0, min(1.0, planet_ratio))))
|
||||
planet_radius = planet_angular_radius_deg(body, float(body_eq.dist))
|
||||
separation = spherical_separation_deg(
|
||||
float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec)
|
||||
)
|
||||
return separation - moon_radius - planet_radius
|
||||
|
||||
|
||||
def moon_planet_overlap_fraction(
|
||||
body: astronomy.Body,
|
||||
observer: astronomy.Observer,
|
||||
dt_utc: datetime,
|
||||
) -> float:
|
||||
time_value = dt_to_time(dt_utc)
|
||||
moon_eq = astronomy.Equator(astronomy.Body.Moon, time_value, observer, True, True)
|
||||
body_eq = astronomy.Equator(body, time_value, observer, True, True)
|
||||
return circle_overlap_fraction(
|
||||
moon_angular_radius_deg(float(moon_eq.dist)),
|
||||
planet_angular_radius_deg(body, float(body_eq.dist)),
|
||||
moon_planet_separation_deg(body, observer, dt_utc),
|
||||
)
|
||||
|
||||
|
||||
def refine_occultation_contact(
|
||||
body: astronomy.Body,
|
||||
observer: astronomy.Observer,
|
||||
@@ -3383,8 +3396,8 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
|
||||
continue
|
||||
|
||||
seen_ranges.append((left, right))
|
||||
occultation_margin = moon_planet_occultation_margin_deg(body, observer, min_time_utc)
|
||||
if occultation_margin <= 0.0:
|
||||
overlap_fraction = moon_planet_overlap_fraction(body, observer, min_time_utc)
|
||||
if overlap_fraction >= (1.0 / 3.0):
|
||||
contact_times = [
|
||||
("start", refine_occultation_contact(body, observer, min_time_utc, -1), "Beginn der Bedeckung"),
|
||||
("maximum", min_time_utc, "Größte Bedeckung"),
|
||||
@@ -3407,6 +3420,19 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
|
||||
})
|
||||
continue
|
||||
|
||||
if overlap_fraction > 0.0:
|
||||
approaches.append({
|
||||
"planet_key": key,
|
||||
"planet_label": label,
|
||||
"label": f"Mond streift {label}",
|
||||
"separation_deg": float(min_sep),
|
||||
"utc_iso": min_time_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"),
|
||||
})
|
||||
continue
|
||||
|
||||
approaches.append({
|
||||
"planet_key": key,
|
||||
"planet_label": label,
|
||||
@@ -3625,12 +3651,13 @@ def action_moon_deep_sky_approaches_for_month(args: list[str]) -> dict:
|
||||
coarse_threshold = max_sep_deg + 1.0
|
||||
|
||||
target_defs = [
|
||||
("pleiades", "Plejaden", 3.7833, 24.1167),
|
||||
("praesepe", "Praesepe", 8.6667, 19.9833),
|
||||
# Katalogisierte major_axis-Werte aus dso_objects, in Grad.
|
||||
("pleiades", "Plejaden", 3.7833, 24.1167, 60.0 / 60.0),
|
||||
("praesepe", "Praesepe", 8.6667, 19.9833, 108.6 / 60.0),
|
||||
]
|
||||
|
||||
approaches = []
|
||||
for key, label, ra_hours, dec_deg in target_defs:
|
||||
for key, label, ra_hours, dec_deg, diameter_deg in target_defs:
|
||||
samples: list[tuple[datetime, float]] = []
|
||||
current = utc_start
|
||||
while current <= utc_end:
|
||||
@@ -3664,10 +3691,22 @@ def action_moon_deep_sky_approaches_for_month(args: list[str]) -> dict:
|
||||
continue
|
||||
|
||||
seen_ranges.append((left, right))
|
||||
moon_eq = astronomy.Equator(astronomy.Body.Moon, dt_to_time(min_time_utc), observer, True, True)
|
||||
overlap_fraction = moon_disk_overlap_fraction(
|
||||
diameter_deg / 2.0,
|
||||
moon_angular_radius_deg(float(moon_eq.dist)),
|
||||
min_sep,
|
||||
)
|
||||
if overlap_fraction >= (1.0 / 3.0):
|
||||
event_label = f"Mond bedeckt {label}"
|
||||
elif overlap_fraction > 0.0:
|
||||
event_label = f"Mond streift {label}"
|
||||
else:
|
||||
event_label = f"Mond nahe {label}"
|
||||
approaches.append({
|
||||
"target_key": key,
|
||||
"target_label": label,
|
||||
"label": f"Mond nahe {label}",
|
||||
"label": event_label,
|
||||
"separation_deg": float(min_sep),
|
||||
"utc_iso": min_time_utc.isoformat().replace("+00:00", "Z"),
|
||||
"local_iso": local_dt.isoformat(),
|
||||
@@ -5133,6 +5172,45 @@ def moon_angular_radius_deg(distance_au: float) -> float:
|
||||
return math.degrees(math.asin(ratio))
|
||||
|
||||
|
||||
def planet_angular_radius_deg(body: astronomy.Body, distance_au: float) -> float:
|
||||
if not math.isfinite(distance_au) or distance_au <= 0:
|
||||
return 0.0
|
||||
ratio = PLANET_RADIUS_KM[body] / (distance_au * astronomy.KM_PER_AU)
|
||||
ratio = max(-1.0, min(1.0, ratio))
|
||||
return math.degrees(math.asin(ratio))
|
||||
|
||||
|
||||
def circle_overlap_fraction(moon_radius_deg: float, target_radius_deg: float, separation_deg: float) -> float:
|
||||
"""Berechnet den Anteil der Zielscheibe, den der Mond überdeckt."""
|
||||
if moon_radius_deg <= 0.0 or target_radius_deg <= 0.0:
|
||||
return 0.0
|
||||
if separation_deg >= moon_radius_deg + target_radius_deg:
|
||||
return 0.0
|
||||
if separation_deg <= abs(moon_radius_deg - target_radius_deg):
|
||||
overlap_area = math.pi * min(moon_radius_deg, target_radius_deg) ** 2
|
||||
else:
|
||||
moon_r2 = moon_radius_deg ** 2
|
||||
target_r2 = target_radius_deg ** 2
|
||||
moon_angle = math.acos((separation_deg ** 2 + moon_r2 - target_r2) / (2.0 * separation_deg * moon_radius_deg))
|
||||
target_angle = math.acos((separation_deg ** 2 + target_r2 - moon_r2) / (2.0 * separation_deg * target_radius_deg))
|
||||
triangle = 0.5 * math.sqrt(max(0.0, (
|
||||
-separation_deg + moon_radius_deg + target_radius_deg
|
||||
) * (
|
||||
separation_deg + moon_radius_deg - target_radius_deg
|
||||
) * (
|
||||
separation_deg - moon_radius_deg + target_radius_deg
|
||||
) * (
|
||||
separation_deg + moon_radius_deg + target_radius_deg
|
||||
)))
|
||||
overlap_area = moon_r2 * moon_angle + target_r2 * target_angle - triangle
|
||||
return max(0.0, min(1.0, overlap_area / (math.pi * target_radius_deg ** 2)))
|
||||
|
||||
|
||||
def moon_disk_overlap_fraction(target_radius_deg: float, moon_radius_deg: float, separation_deg: float) -> float:
|
||||
"""Berechnet bei Sternhaufen den überdeckten Anteil der Mondscheibe."""
|
||||
return circle_overlap_fraction(target_radius_deg, moon_radius_deg, separation_deg)
|
||||
|
||||
|
||||
def normalize_delta_ra_hours(delta_ra_hours: float) -> float:
|
||||
while delta_ra_hours > 12.0:
|
||||
delta_ra_hours -= 24.0
|
||||
|
||||
Reference in New Issue
Block a user