Monatsvorschau weiter gebaut
This commit is contained in:
+300
-2
@@ -384,6 +384,289 @@ def action_month_sky_context(args: list[str]) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def action_moon_phases_for_month(args: list[str]) -> dict:
|
||||
if len(args) != 3:
|
||||
fail(
|
||||
"Aktion moon_phases_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)
|
||||
|
||||
search_start = dt_to_time(local_start.astimezone(timezone.utc) - timedelta(days=3))
|
||||
search_limit_days = ((local_end - local_start).total_seconds() / 86400.0) + 10.0
|
||||
|
||||
phase_defs = [
|
||||
(0.0, "Neumond"),
|
||||
(90.0, "Erstes Viertel"),
|
||||
(180.0, "Vollmond"),
|
||||
(270.0, "Letztes Viertel"),
|
||||
]
|
||||
|
||||
phases = []
|
||||
for target_lon, label in phase_defs:
|
||||
probe = search_start
|
||||
found = None
|
||||
|
||||
for _ in range(3):
|
||||
result = astronomy.SearchMoonPhase(target_lon, probe, search_limit_days)
|
||||
if result is None:
|
||||
break
|
||||
|
||||
dt_utc = time_to_datetime(result)
|
||||
local_dt = dt_utc.astimezone(tz)
|
||||
if local_dt.year == year and local_dt.month == month:
|
||||
found = {
|
||||
"label": label,
|
||||
"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"),
|
||||
"sort_iso": local_dt.isoformat(),
|
||||
}
|
||||
break
|
||||
|
||||
probe = dt_to_time(dt_utc + timedelta(days=1))
|
||||
|
||||
if found is not None:
|
||||
phases.append(found)
|
||||
|
||||
phases.sort(key=lambda item: item["sort_iso"])
|
||||
for item in phases:
|
||||
item.pop("sort_iso", None)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "moon_phases_for_month",
|
||||
"selected": {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"timezone": timezone_name,
|
||||
},
|
||||
"phases": phases,
|
||||
}
|
||||
|
||||
|
||||
def moon_planet_separation_deg(
|
||||
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 spherical_separation_deg(float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec))
|
||||
|
||||
|
||||
def refine_minimum_separation(
|
||||
body: astronomy.Body,
|
||||
observer: astronomy.Observer,
|
||||
left_utc: datetime,
|
||||
right_utc: datetime,
|
||||
) -> tuple[datetime, float]:
|
||||
left = left_utc
|
||||
right = right_utc
|
||||
|
||||
for _ in range(32):
|
||||
span = (right - left) / 3
|
||||
m1 = left + span
|
||||
m2 = right - span
|
||||
f1 = moon_planet_separation_deg(body, observer, m1)
|
||||
f2 = moon_planet_separation_deg(body, observer, m2)
|
||||
if f1 <= f2:
|
||||
right = m2
|
||||
else:
|
||||
left = m1
|
||||
|
||||
best = left + (right - left) / 2
|
||||
return best, moon_planet_separation_deg(body, observer, best)
|
||||
|
||||
|
||||
def action_moon_planet_approaches(args: list[str]) -> dict:
|
||||
if len(args) != 7:
|
||||
fail(
|
||||
"Aktion moon_planet_approaches erwartet 7 Argumente: latitude longitude elevation year month timezone max_sep_deg",
|
||||
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])
|
||||
month = int(args[4])
|
||||
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[5]
|
||||
max_sep_deg = parse_float(args[6], "Maximalabstand")
|
||||
|
||||
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, 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)
|
||||
utc_end = local_end.astimezone(timezone.utc)
|
||||
scan_step = timedelta(hours=1)
|
||||
coarse_threshold = max_sep_deg + 1.0
|
||||
|
||||
planet_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"),
|
||||
]
|
||||
|
||||
approaches = []
|
||||
for label, body, key in planet_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:
|
||||
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)
|
||||
local_dt = min_time_utc.astimezone(tz)
|
||||
if local_dt.year != year or local_dt.month != month:
|
||||
continue
|
||||
if min_sep > max_sep_deg:
|
||||
continue
|
||||
|
||||
seen_ranges.append((left, right))
|
||||
approaches.append({
|
||||
"planet_key": key,
|
||||
"planet_label": label,
|
||||
"label": f"Mond nahe {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"),
|
||||
})
|
||||
|
||||
approaches.sort(key=lambda item: item["local_iso"])
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "moon_planet_approaches",
|
||||
"selected": {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"timezone": timezone_name,
|
||||
"max_separation_deg": max_sep_deg,
|
||||
},
|
||||
"approaches": approaches,
|
||||
}
|
||||
|
||||
|
||||
def action_moon_star_occultations_for_month(args: list[str]) -> dict:
|
||||
if len(args) != 7:
|
||||
fail(
|
||||
"Aktion moon_star_occultations_for_month erwartet 7 Argumente: latitude longitude elevation year month timezone star_file",
|
||||
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])
|
||||
month = int(args[4])
|
||||
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[5]
|
||||
star_file_path = args[6]
|
||||
if not star_file_path:
|
||||
fail("Sterndatei fehlt.")
|
||||
|
||||
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)
|
||||
star_rows = load_star_file(star_file_path)
|
||||
|
||||
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)
|
||||
|
||||
results, debug_profile = compute_star_occultation(star_rows, observer, local_start, local_end, tz)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "moon_star_occultations_for_month",
|
||||
"selected": {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"timezone": timezone_name,
|
||||
},
|
||||
"star_count": len(star_rows),
|
||||
"debug": debug_profile,
|
||||
"results": results,
|
||||
}
|
||||
|
||||
|
||||
def classify_visibility(sun_alt_deg: float) -> dict:
|
||||
if sun_alt_deg > -5:
|
||||
return {"key": "day", "label": "Tag"}
|
||||
@@ -1385,7 +1668,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"]})
|
||||
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", "moon_planet_approaches", "moon_star_occultations_for_month"]})
|
||||
|
||||
action = sys.argv[1]
|
||||
args = sys.argv[2:]
|
||||
@@ -1420,7 +1703,22 @@ def main() -> None:
|
||||
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"]})
|
||||
if action == "moon_phases_for_month":
|
||||
result = action_moon_phases_for_month(args)
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
if action == "moon_planet_approaches":
|
||||
result = action_moon_planet_approaches(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", "moon_planet_approaches", "moon_star_occultations_for_month"]})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user