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'));
|
$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
|
// Alle Mondphasen und alle Mondereignisse werden vollständig übernommen.
|
||||||
// werden weiterhin begrenzt, damit der Übersichtstext kompakt bleibt.
|
$nonPhaseCandidates = array_values(array_filter(
|
||||||
$nonPhaseEvents = array_slice(
|
$sortedMoonEvents,
|
||||||
array_values(array_filter($sortedMoonEvents, static fn (array $e): bool => (string) ($e['type'] ?? '') !== 'moon_phase')),
|
static fn (array $e): bool => (string) ($e['type'] ?? '') !== 'moon_phase'
|
||||||
0,
|
));
|
||||||
10
|
$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 = [];
|
$moonSentences = [];
|
||||||
|
|
||||||
@@ -985,7 +988,7 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($goldenHandle !== null) {
|
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) {
|
if ($apsis !== null) {
|
||||||
@@ -1057,6 +1060,9 @@ if (!function_exists('monthForecastBuildNarrativeText')) {
|
|||||||
if (str_starts_with($apLabel, 'Mond nahe ')) {
|
if (str_starts_with($apLabel, 'Mond nahe ')) {
|
||||||
$target = substr($apLabel, strlen('Mond nahe '));
|
$target = substr($apLabel, strlen('Mond nahe '));
|
||||||
$moonSentences[] = 'Am ' . $apSd . ' zieht der Mond' . $sepStr . ' an ' . $target . ' vorbei.';
|
$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 ')) {
|
} elseif (str_starts_with($apLabel, 'Mond bedeckt ')) {
|
||||||
$target = substr($apLabel, strlen('Mond bedeckt '));
|
$target = substr($apLabel, strlen('Mond bedeckt '));
|
||||||
$moonSentences[] = 'Am ' . $apSd . ' bedeckt der Mond ' . $target . '.';
|
$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)
|
moon_eq = astronomy.Equator(astronomy.Body.Moon, time_value, observer, True, True)
|
||||||
body_eq = astronomy.Equator(body, 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))
|
moon_radius = moon_angular_radius_deg(float(moon_eq.dist))
|
||||||
planet_radius_km = PLANET_RADIUS_KM[body]
|
planet_radius = planet_angular_radius_deg(body, float(body_eq.dist))
|
||||||
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))))
|
|
||||||
separation = spherical_separation_deg(
|
separation = spherical_separation_deg(
|
||||||
float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec)
|
float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec)
|
||||||
)
|
)
|
||||||
return separation - moon_radius - planet_radius
|
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(
|
def refine_occultation_contact(
|
||||||
body: astronomy.Body,
|
body: astronomy.Body,
|
||||||
observer: astronomy.Observer,
|
observer: astronomy.Observer,
|
||||||
@@ -3383,8 +3396,8 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
seen_ranges.append((left, right))
|
seen_ranges.append((left, right))
|
||||||
occultation_margin = moon_planet_occultation_margin_deg(body, observer, min_time_utc)
|
overlap_fraction = moon_planet_overlap_fraction(body, observer, min_time_utc)
|
||||||
if occultation_margin <= 0.0:
|
if overlap_fraction >= (1.0 / 3.0):
|
||||||
contact_times = [
|
contact_times = [
|
||||||
("start", refine_occultation_contact(body, observer, min_time_utc, -1), "Beginn der Bedeckung"),
|
("start", refine_occultation_contact(body, observer, min_time_utc, -1), "Beginn der Bedeckung"),
|
||||||
("maximum", min_time_utc, "Größte Bedeckung"),
|
("maximum", min_time_utc, "Größte Bedeckung"),
|
||||||
@@ -3407,6 +3420,19 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
|
|||||||
})
|
})
|
||||||
continue
|
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({
|
approaches.append({
|
||||||
"planet_key": key,
|
"planet_key": key,
|
||||||
"planet_label": label,
|
"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
|
coarse_threshold = max_sep_deg + 1.0
|
||||||
|
|
||||||
target_defs = [
|
target_defs = [
|
||||||
("pleiades", "Plejaden", 3.7833, 24.1167),
|
# Katalogisierte major_axis-Werte aus dso_objects, in Grad.
|
||||||
("praesepe", "Praesepe", 8.6667, 19.9833),
|
("pleiades", "Plejaden", 3.7833, 24.1167, 60.0 / 60.0),
|
||||||
|
("praesepe", "Praesepe", 8.6667, 19.9833, 108.6 / 60.0),
|
||||||
]
|
]
|
||||||
|
|
||||||
approaches = []
|
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]] = []
|
samples: list[tuple[datetime, float]] = []
|
||||||
current = utc_start
|
current = utc_start
|
||||||
while current <= utc_end:
|
while current <= utc_end:
|
||||||
@@ -3664,10 +3691,22 @@ def action_moon_deep_sky_approaches_for_month(args: list[str]) -> dict:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
seen_ranges.append((left, right))
|
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({
|
approaches.append({
|
||||||
"target_key": key,
|
"target_key": key,
|
||||||
"target_label": label,
|
"target_label": label,
|
||||||
"label": f"Mond nahe {label}",
|
"label": event_label,
|
||||||
"separation_deg": float(min_sep),
|
"separation_deg": float(min_sep),
|
||||||
"utc_iso": min_time_utc.isoformat().replace("+00:00", "Z"),
|
"utc_iso": min_time_utc.isoformat().replace("+00:00", "Z"),
|
||||||
"local_iso": local_dt.isoformat(),
|
"local_iso": local_dt.isoformat(),
|
||||||
@@ -5133,6 +5172,45 @@ def moon_angular_radius_deg(distance_au: float) -> float:
|
|||||||
return math.degrees(math.asin(ratio))
|
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:
|
def normalize_delta_ra_hours(delta_ra_hours: float) -> float:
|
||||||
while delta_ra_hours > 12.0:
|
while delta_ra_hours > 12.0:
|
||||||
delta_ra_hours -= 24.0
|
delta_ra_hours -= 24.0
|
||||||
|
|||||||
Reference in New Issue
Block a user