Jupitermonde in MV eingabuet
This commit is contained in:
+3240
-296
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,7 @@ $innerPlanetElongationEvents = [];
|
||||
$venusPeakMagnitudeEvents = [];
|
||||
$outerPlanetEvents = [];
|
||||
$outerPlanetStationEvents = [];
|
||||
$jupiterMoonSideEvents = [];
|
||||
$moonOccultations = [];
|
||||
$meteorShowerPeaks = [];
|
||||
$seasonChanges = [];
|
||||
@@ -616,6 +617,23 @@ if (is_array($outerPlanetStationDecoded) && ($outerPlanetStationDecoded['ok'] ??
|
||||
$outerPlanetStationEvents = is_array($outerPlanetStationDecoded['events'] ?? null) ? $outerPlanetStationDecoded['events'] : [];
|
||||
}
|
||||
|
||||
$jupiterMoonSideOutput = [];
|
||||
$jupiterMoonSideExitCode = 0;
|
||||
$jupiterMoonSideArgs = [
|
||||
$pythonScriptPath,
|
||||
'jupiter_moons_one_side_for_month',
|
||||
(string) $selectedYear,
|
||||
(string) $selectedMonth,
|
||||
$chartTimezone,
|
||||
];
|
||||
$jupiterMoonSideCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $jupiterMoonSideArgs)) . ' 2>&1';
|
||||
exec($jupiterMoonSideCommand, $jupiterMoonSideOutput, $jupiterMoonSideExitCode);
|
||||
$jupiterMoonSideRaw = trim(implode("\n", $jupiterMoonSideOutput));
|
||||
$jupiterMoonSideDecoded = json_decode($jupiterMoonSideRaw, true);
|
||||
if (is_array($jupiterMoonSideDecoded) && ($jupiterMoonSideDecoded['ok'] ?? false)) {
|
||||
$jupiterMoonSideEvents = is_array($jupiterMoonSideDecoded['events'] ?? null) ? $jupiterMoonSideDecoded['events'] : [];
|
||||
}
|
||||
|
||||
$moonPlanetOutput = [];
|
||||
$moonPlanetExitCode = 0;
|
||||
$moonPlanetArgs = [
|
||||
@@ -930,6 +948,26 @@ foreach ($outerPlanetStationEvents as $outerPlanetStationEvent) {
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($jupiterMoonSideEvents as $jupiterMoonSideEvent) {
|
||||
if (!is_array($jupiterMoonSideEvent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$durationLabel = trim((string) ($jupiterMoonSideEvent['duration_label'] ?? ''));
|
||||
$eventLabel = trim((string) ($jupiterMoonSideEvent['label'] ?? 'Alle 4 Jupitermonde auf einer Seite von Jupiter'));
|
||||
if ($durationLabel !== '') {
|
||||
$eventLabel .= ' (' . $durationLabel . ')';
|
||||
}
|
||||
|
||||
$monthEventList[] = [
|
||||
'event' => $eventLabel,
|
||||
'date' => (string) ($jupiterMoonSideEvent['local_date'] ?? ''),
|
||||
'time' => (string) ($jupiterMoonSideEvent['local_time'] ?? ''),
|
||||
'local_iso' => (string) ($jupiterMoonSideEvent['local_iso'] ?? ''),
|
||||
'type' => 'jupiter_moons_one_side',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($moonPlanetApproaches as $approachEvent) {
|
||||
if (!is_array($approachEvent)) {
|
||||
continue;
|
||||
|
||||
Binary file not shown.
+327
-2
@@ -1759,6 +1759,326 @@ def action_outer_planet_stations_for_month(args: list[str]) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def action_jupiter_moons_one_side_for_month(args: list[str]) -> dict:
|
||||
if len(args) != 3:
|
||||
fail(
|
||||
"Aktion jupiter_moons_one_side_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)
|
||||
utc_end = local_end.astimezone(timezone.utc)
|
||||
scan_step = timedelta(minutes=10)
|
||||
x_tolerance_au = 1.0e-6
|
||||
jupiter_radius_au = astronomy.JUPITER_EQUATORIAL_RADIUS_KM / astronomy.KM_PER_AU
|
||||
moon_keys = ["io", "europa", "ganymede", "callisto"]
|
||||
|
||||
def classify_side(dt_utc: datetime) -> tuple[str | None, str | None, dict[str, float], dict[str, float]]:
|
||||
time_value = dt_to_time(dt_utc)
|
||||
rotation_eqj_to_ecl = astronomy.Rotation_EQJ_ECL()
|
||||
jupiter_geo = astronomy.GeoVector(astronomy.Body.Jupiter, time_value, True)
|
||||
jupiter_helio = astronomy.HelioVector(astronomy.Body.Jupiter, time_value)
|
||||
moon_states = astronomy.JupiterMoons(time_value)
|
||||
to_earth_eqj = astronomy.Vector(
|
||||
-float(jupiter_geo.x),
|
||||
-float(jupiter_geo.y),
|
||||
-float(jupiter_geo.z),
|
||||
time_value,
|
||||
)
|
||||
to_earth_ecl = astronomy.RotateVector(rotation_eqj_to_ecl, to_earth_eqj)
|
||||
|
||||
camera_x = float(to_earth_ecl.x)
|
||||
camera_y = float(to_earth_ecl.z)
|
||||
camera_z = -float(to_earth_ecl.y)
|
||||
camera_len = math.sqrt(
|
||||
(camera_x * camera_x) + (camera_y * camera_y) + (camera_z * camera_z)
|
||||
)
|
||||
if camera_len <= 1.0e-12:
|
||||
return None, None, {}, {}
|
||||
|
||||
# Dieselbe Welt-/Kameraorientierung wie in jupitersystem.php:
|
||||
# Display-Koordinaten sind (x, z, -y), die Kamera schaut aus der Richtung "toEarth".
|
||||
forward_x = -camera_x / camera_len
|
||||
forward_y = -camera_y / camera_len
|
||||
forward_z = -camera_z / camera_len
|
||||
|
||||
up_x = 0.0
|
||||
up_y = 1.0
|
||||
up_z = 0.0
|
||||
|
||||
right_x = (up_y * forward_z) - (up_z * forward_y)
|
||||
right_y = (up_z * forward_x) - (up_x * forward_z)
|
||||
right_z = (up_x * forward_y) - (up_y * forward_x)
|
||||
right_len = math.sqrt(
|
||||
(right_x * right_x) + (right_y * right_y) + (right_z * right_z)
|
||||
)
|
||||
if right_len <= 1.0e-12:
|
||||
return None, None, {}, {}
|
||||
|
||||
right_x /= right_len
|
||||
right_y /= right_len
|
||||
right_z /= right_len
|
||||
|
||||
# Himmlische Ostrichtung am Jupiter-Ort fuer die textliche Richtung oestlich/westlich.
|
||||
jx = float(jupiter_geo.x)
|
||||
jy = float(jupiter_geo.y)
|
||||
east_eqj_x = -jy
|
||||
east_eqj_y = jx
|
||||
east_eqj_z = 0.0
|
||||
east_eqj_len = math.sqrt(
|
||||
(east_eqj_x * east_eqj_x) + (east_eqj_y * east_eqj_y) + (east_eqj_z * east_eqj_z)
|
||||
)
|
||||
if east_eqj_len <= 1.0e-12:
|
||||
return None, None, {}, {}
|
||||
|
||||
east_eqj_x /= east_eqj_len
|
||||
east_eqj_y /= east_eqj_len
|
||||
east_eqj_z /= east_eqj_len
|
||||
|
||||
east_ecl = astronomy.RotateVector(
|
||||
rotation_eqj_to_ecl,
|
||||
astronomy.Vector(east_eqj_x, east_eqj_y, east_eqj_z, time_value),
|
||||
)
|
||||
east_display_x = float(east_ecl.x)
|
||||
east_display_y = float(east_ecl.z)
|
||||
east_display_z = -float(east_ecl.y)
|
||||
east_screen_x = (
|
||||
(east_display_x * right_x)
|
||||
+ (east_display_y * right_y)
|
||||
+ (east_display_z * right_z)
|
||||
)
|
||||
|
||||
sun_to_jupiter_x = float(jupiter_helio.x)
|
||||
sun_to_jupiter_y = float(jupiter_helio.y)
|
||||
sun_to_jupiter_z = float(jupiter_helio.z)
|
||||
sun_to_jupiter_len = math.sqrt(
|
||||
(sun_to_jupiter_x * sun_to_jupiter_x)
|
||||
+ (sun_to_jupiter_y * sun_to_jupiter_y)
|
||||
+ (sun_to_jupiter_z * sun_to_jupiter_z)
|
||||
)
|
||||
if sun_to_jupiter_len <= 1.0e-12:
|
||||
return None, None, {}, {}
|
||||
|
||||
sun_to_jupiter_x /= sun_to_jupiter_len
|
||||
sun_to_jupiter_y /= sun_to_jupiter_len
|
||||
sun_to_jupiter_z /= sun_to_jupiter_len
|
||||
|
||||
projected_x_positions = {}
|
||||
east_offsets = {}
|
||||
shadow_flags = {}
|
||||
for key in moon_keys:
|
||||
moon_state = getattr(moon_states, key)
|
||||
moon_ecl = astronomy.RotateVector(rotation_eqj_to_ecl, moon_state)
|
||||
moon_display_x = float(moon_ecl.x)
|
||||
moon_display_y = float(moon_ecl.z)
|
||||
moon_display_z = -float(moon_ecl.y)
|
||||
projected_x = (
|
||||
(moon_display_x * right_x)
|
||||
+ (moon_display_y * right_y)
|
||||
+ (moon_display_z * right_z)
|
||||
)
|
||||
projected_x_positions[key] = projected_x
|
||||
east_offsets[key] = projected_x * east_screen_x
|
||||
|
||||
moon_x = float(moon_state.x)
|
||||
moon_y = float(moon_state.y)
|
||||
moon_z = float(moon_state.z)
|
||||
shadow_axis_distance = (
|
||||
(moon_x * sun_to_jupiter_x)
|
||||
+ (moon_y * sun_to_jupiter_y)
|
||||
+ (moon_z * sun_to_jupiter_z)
|
||||
)
|
||||
perp_x = moon_x - (shadow_axis_distance * sun_to_jupiter_x)
|
||||
perp_y = moon_y - (shadow_axis_distance * sun_to_jupiter_y)
|
||||
perp_z = moon_z - (shadow_axis_distance * sun_to_jupiter_z)
|
||||
shadow_flags[key] = (
|
||||
shadow_axis_distance > 0.0
|
||||
and math.sqrt((perp_x * perp_x) + (perp_y * perp_y) + (perp_z * perp_z)) < jupiter_radius_au
|
||||
)
|
||||
|
||||
projected_side_kind = None
|
||||
minimum_positive_x = jupiter_radius_au + x_tolerance_au
|
||||
|
||||
if (
|
||||
all(x_value > minimum_positive_x for x_value in projected_x_positions.values())
|
||||
and not any(shadow_flags.values())
|
||||
):
|
||||
projected_side_kind = "right"
|
||||
elif (
|
||||
all(x_value < -minimum_positive_x for x_value in projected_x_positions.values())
|
||||
and not any(shadow_flags.values())
|
||||
):
|
||||
projected_side_kind = "left"
|
||||
|
||||
east_west_kind = None
|
||||
if all(offset > x_tolerance_au for offset in east_offsets.values()):
|
||||
east_west_kind = "east"
|
||||
elif all(offset < -x_tolerance_au for offset in east_offsets.values()):
|
||||
east_west_kind = "west"
|
||||
|
||||
return projected_side_kind, east_west_kind, projected_x_positions, east_offsets
|
||||
|
||||
def refine_transition(
|
||||
left_utc: datetime,
|
||||
right_utc: datetime,
|
||||
target_kind: str,
|
||||
*,
|
||||
find_start: bool,
|
||||
) -> datetime:
|
||||
left = left_utc
|
||||
right = right_utc
|
||||
|
||||
for _ in range(32):
|
||||
mid = left + (right - left) / 2
|
||||
mid_kind, _, _, _ = classify_side(mid)
|
||||
if find_start:
|
||||
if mid_kind == target_kind:
|
||||
right = mid
|
||||
else:
|
||||
left = mid
|
||||
else:
|
||||
if mid_kind == target_kind:
|
||||
left = mid
|
||||
else:
|
||||
right = mid
|
||||
|
||||
return left + (right - left) / 2
|
||||
|
||||
def format_duration_label(duration_minutes: int) -> str:
|
||||
hours, minutes = divmod(max(0, duration_minutes), 60)
|
||||
if hours > 0 and minutes > 0:
|
||||
return f"{hours} h {minutes:02d} min"
|
||||
if hours > 0:
|
||||
return f"{hours} h"
|
||||
return f"{minutes} min"
|
||||
|
||||
def direction_text_from_kinds(raw_kind: str | None, east_west_kind: str | None) -> str:
|
||||
if east_west_kind == "east":
|
||||
return "oestlich"
|
||||
if east_west_kind == "west":
|
||||
return "westlich"
|
||||
return "westlich" if raw_kind == "left" else "oestlich"
|
||||
|
||||
events: list[dict] = []
|
||||
current = utc_start
|
||||
active_kind, active_east_west_kind, _, _ = classify_side(current)
|
||||
active_start_utc = utc_start if active_kind is not None else None
|
||||
previous_time = current
|
||||
previous_kind = active_kind
|
||||
current += scan_step
|
||||
|
||||
while current <= utc_end:
|
||||
current_kind, current_east_west_kind, _, _ = classify_side(current)
|
||||
|
||||
if active_kind is None and previous_kind != current_kind and current_kind is not None:
|
||||
active_kind = current_kind
|
||||
active_east_west_kind = current_east_west_kind
|
||||
active_start_utc = refine_transition(previous_time, current, current_kind, find_start=True)
|
||||
|
||||
elif active_kind is not None and current_kind != active_kind:
|
||||
active_end_utc = refine_transition(previous_time, current, active_kind, find_start=False)
|
||||
clamped_start_utc = max(active_start_utc or utc_start, utc_start)
|
||||
clamped_end_utc = min(active_end_utc, utc_end)
|
||||
|
||||
if clamped_end_utc > clamped_start_utc:
|
||||
start_local = clamped_start_utc.astimezone(tz)
|
||||
end_local = clamped_end_utc.astimezone(tz)
|
||||
duration_minutes = max(
|
||||
1,
|
||||
int(round((clamped_end_utc - clamped_start_utc).total_seconds() / 60.0)),
|
||||
)
|
||||
side_text = direction_text_from_kinds(active_kind, active_east_west_kind)
|
||||
events.append({
|
||||
"kind": active_kind,
|
||||
"direction": active_east_west_kind,
|
||||
"label": f"Alle 4 Jupitermonde {side_text} von Jupiter",
|
||||
"duration_label": format_duration_label(duration_minutes),
|
||||
"duration_minutes": duration_minutes,
|
||||
"utc_iso": clamped_start_utc.isoformat().replace("+00:00", "Z"),
|
||||
"local_iso": start_local.isoformat(),
|
||||
"local_date": start_local.strftime("%d.%m.%Y"),
|
||||
"local_time": start_local.strftime("%H:%M"),
|
||||
"end_utc_iso": clamped_end_utc.isoformat().replace("+00:00", "Z"),
|
||||
"end_local_iso": end_local.isoformat(),
|
||||
"end_local_date": end_local.strftime("%d.%m.%Y"),
|
||||
"end_local_time": end_local.strftime("%H:%M"),
|
||||
})
|
||||
|
||||
active_kind = None
|
||||
active_east_west_kind = None
|
||||
active_start_utc = None
|
||||
|
||||
if current_kind is not None:
|
||||
active_kind = current_kind
|
||||
active_east_west_kind = current_east_west_kind
|
||||
active_start_utc = refine_transition(previous_time, current, current_kind, find_start=True)
|
||||
|
||||
previous_time = current
|
||||
previous_kind = current_kind
|
||||
current += scan_step
|
||||
|
||||
if active_kind is not None and active_start_utc is not None:
|
||||
clamped_start_utc = max(active_start_utc, utc_start)
|
||||
clamped_end_utc = utc_end
|
||||
if clamped_end_utc > clamped_start_utc:
|
||||
start_local = clamped_start_utc.astimezone(tz)
|
||||
end_local = clamped_end_utc.astimezone(tz)
|
||||
duration_minutes = max(
|
||||
1,
|
||||
int(round((clamped_end_utc - clamped_start_utc).total_seconds() / 60.0)),
|
||||
)
|
||||
side_text = direction_text_from_kinds(active_kind, active_east_west_kind)
|
||||
events.append({
|
||||
"kind": active_kind,
|
||||
"direction": active_east_west_kind,
|
||||
"label": f"Alle 4 Jupitermonde {side_text} von Jupiter",
|
||||
"duration_label": format_duration_label(duration_minutes),
|
||||
"duration_minutes": duration_minutes,
|
||||
"utc_iso": clamped_start_utc.isoformat().replace("+00:00", "Z"),
|
||||
"local_iso": start_local.isoformat(),
|
||||
"local_date": start_local.strftime("%d.%m.%Y"),
|
||||
"local_time": start_local.strftime("%H:%M"),
|
||||
"end_utc_iso": clamped_end_utc.isoformat().replace("+00:00", "Z"),
|
||||
"end_local_iso": end_local.isoformat(),
|
||||
"end_local_date": end_local.strftime("%d.%m.%Y"),
|
||||
"end_local_time": end_local.strftime("%H:%M"),
|
||||
})
|
||||
|
||||
events.sort(key=lambda item: item["local_iso"])
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "jupiter_moons_one_side_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(
|
||||
@@ -2815,7 +3135,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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "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", "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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "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", "moon_star_occultations_for_month"]})
|
||||
|
||||
action = sys.argv[1]
|
||||
args = sys.argv[2:]
|
||||
@@ -2915,12 +3235,17 @@ def main() -> None:
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
if action == "jupiter_moons_one_side_for_month":
|
||||
result = action_jupiter_moons_one_side_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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "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", "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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "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", "moon_star_occultations_for_month"]})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user