Konjunktionen von Planeten eingebaut

This commit is contained in:
Eskimue
2026-04-03 13:35:20 +02:00
parent 3d98fef789
commit 81176fb81a
15 changed files with 318 additions and 3692 deletions
File diff suppressed because it is too large Load Diff
View File
+92 -76
View File
@@ -17,6 +17,7 @@ $starLabelMagnitudeLimit = 2.0;
$messierDatabaseMagnitudeLimit = 4.0; $messierDatabaseMagnitudeLimit = 4.0;
$messierDisplayMagnitudeLimit = 1.0; $messierDisplayMagnitudeLimit = 1.0;
$moonPlanetMaxSeparationDeg = 1.5; $moonPlanetMaxSeparationDeg = 1.5;
$planetConjunctionMaxSeparationDeg = 1.0;
$moonOccultationMaxMag = 2.0; $moonOccultationMaxMag = 2.0;
$years = range($currentYear - 2, $currentYear + 5); $years = range($currentYear - 2, $currentYear + 5);
@@ -57,7 +58,9 @@ $planetData = [];
$moonData = null; $moonData = null;
$moonPhaseEvents = []; $moonPhaseEvents = [];
$moonPlanetApproaches = []; $moonPlanetApproaches = [];
$planetConjunctions = [];
$moonOccultations = []; $moonOccultations = [];
$seasonChanges = [];
$monthEventList = []; $monthEventList = [];
$starDataError = null; $starDataError = null;
@@ -327,6 +330,23 @@ if (is_array($moonPhaseDecoded) && ($moonPhaseDecoded['ok'] ?? false)) {
$moonPhaseEvents = is_array($moonPhaseDecoded['phases'] ?? null) ? $moonPhaseDecoded['phases'] : []; $moonPhaseEvents = is_array($moonPhaseDecoded['phases'] ?? null) ? $moonPhaseDecoded['phases'] : [];
} }
$seasonOutput = [];
$seasonExitCode = 0;
$seasonArgs = [
$pythonScriptPath,
'season_changes_for_month',
(string) $selectedYear,
(string) $selectedMonth,
$chartTimezone,
];
$seasonCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $seasonArgs)) . ' 2>&1';
exec($seasonCommand, $seasonOutput, $seasonExitCode);
$seasonRaw = trim(implode("\n", $seasonOutput));
$seasonDecoded = json_decode($seasonRaw, true);
if (is_array($seasonDecoded) && ($seasonDecoded['ok'] ?? false)) {
$seasonChanges = is_array($seasonDecoded['changes'] ?? null) ? $seasonDecoded['changes'] : [];
}
$moonPlanetOutput = []; $moonPlanetOutput = [];
$moonPlanetExitCode = 0; $moonPlanetExitCode = 0;
$moonPlanetArgs = [ $moonPlanetArgs = [
@@ -348,6 +368,27 @@ if (is_array($moonPlanetDecoded) && ($moonPlanetDecoded['ok'] ?? false)) {
$moonPlanetApproaches = is_array($moonPlanetDecoded['approaches'] ?? null) ? $moonPlanetDecoded['approaches'] : []; $moonPlanetApproaches = is_array($moonPlanetDecoded['approaches'] ?? null) ? $moonPlanetDecoded['approaches'] : [];
} }
$planetConjunctionOutput = [];
$planetConjunctionExitCode = 0;
$planetConjunctionArgs = [
$pythonScriptPath,
'planet_conjunctions_for_month',
(string) $chartLatitude,
(string) $chartLongitude,
(string) $chartElevation,
(string) $selectedYear,
(string) $selectedMonth,
$chartTimezone,
(string) $planetConjunctionMaxSeparationDeg,
];
$planetConjunctionCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $planetConjunctionArgs)) . ' 2>&1';
exec($planetConjunctionCommand, $planetConjunctionOutput, $planetConjunctionExitCode);
$planetConjunctionRaw = trim(implode("\n", $planetConjunctionOutput));
$planetConjunctionDecoded = json_decode($planetConjunctionRaw, true);
if (is_array($planetConjunctionDecoded) && ($planetConjunctionDecoded['ok'] ?? false)) {
$planetConjunctions = is_array($planetConjunctionDecoded['conjunctions'] ?? null) ? $planetConjunctionDecoded['conjunctions'] : [];
}
$moonOccultationStars = []; $moonOccultationStars = [];
try { try {
if (isset($pdo)) { if (isset($pdo)) {
@@ -427,13 +468,27 @@ foreach ($moonPhaseEvents as $phaseEvent) {
]; ];
} }
foreach ($seasonChanges as $seasonChange) {
if (!is_array($seasonChange)) {
continue;
}
$monthEventList[] = [
'event' => (string) ($seasonChange['label'] ?? 'Jahreszeitenwechsel'),
'date' => (string) ($seasonChange['local_date'] ?? ''),
'time' => (string) ($seasonChange['local_time'] ?? ''),
'local_iso' => (string) ($seasonChange['local_iso'] ?? ''),
'type' => 'season_change',
];
}
foreach ($moonPlanetApproaches as $approachEvent) { foreach ($moonPlanetApproaches as $approachEvent) {
if (!is_array($approachEvent)) { if (!is_array($approachEvent)) {
continue; continue;
} }
$distanceText = isset($approachEvent['separation_deg']) $distanceText = isset($approachEvent['separation_deg'])
? number_format((float) $approachEvent['separation_deg'], 2, ',', '') . '°' ? number_format((float) $approachEvent['separation_deg'], 2, ',', '') . ' Grad'
: ''; : '';
$monthEventList[] = [ $monthEventList[] = [
@@ -445,6 +500,24 @@ foreach ($moonPlanetApproaches as $approachEvent) {
]; ];
} }
foreach ($planetConjunctions as $conjunctionEvent) {
if (!is_array($conjunctionEvent)) {
continue;
}
$distanceText = isset($conjunctionEvent['separation_deg'])
? number_format((float) $conjunctionEvent['separation_deg'], 2, ',', '') . ' Grad'
: '';
$monthEventList[] = [
'event' => trim((string) ($conjunctionEvent['label'] ?? 'Planetenkonjunktion') . ($distanceText !== '' ? ' (' . $distanceText . ')' : '')),
'date' => (string) ($conjunctionEvent['local_date'] ?? ''),
'time' => (string) ($conjunctionEvent['local_time'] ?? ''),
'local_iso' => (string) ($conjunctionEvent['local_iso'] ?? ''),
'type' => 'planet_conjunction',
];
}
foreach ($moonOccultations as $occultationEvent) { foreach ($moonOccultations as $occultationEvent) {
if (!is_array($occultationEvent)) { if (!is_array($occultationEvent)) {
continue; continue;
@@ -459,7 +532,7 @@ foreach ($moonOccultations as $occultationEvent) {
$starLabel = (string) ($star['label'] ?? 'Stern'); $starLabel = (string) ($star['label'] ?? 'Stern');
$starMag = isset($star['mag']) ? number_format((float) $star['mag'], 1, ',', '') : ''; $starMag = isset($star['mag']) ? number_format((float) $star['mag'], 1, ',', '') : '';
$monthEventList[] = [ $monthEventList[] = [
'event' => 'Mond bedeckt ' . $starLabel . ($starMag !== '' ? ' (mag ' . $starMag . ')' : ''), 'event' => 'Mond bedeckt ' . $starLabel . ($starMag !== '' ? ' (' . $starMag . ' mag)' : ''),
'date' => (string) ($maximum['local_date'] ?? ''), 'date' => (string) ($maximum['local_date'] ?? ''),
'time' => (string) ($maximum['local_time'] ?? ''), 'time' => (string) ($maximum['local_time'] ?? ''),
'local_iso' => (string) ($maximum['local_iso'] ?? ''), 'local_iso' => (string) ($maximum['local_iso'] ?? ''),
@@ -558,7 +631,7 @@ $starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_U
<section class="monatskarte-card"> <section class="monatskarte-card">
<div class="monatskarte-header"> <div class="monatskarte-header">
<div> <div>
<h3>Südhimmel <?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?></h3> <h3>Südhimmel <?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?></h3>
<p id="monatskarteSouthMeta"> <p id="monatskarteSouthMeta">
<?= htmlspecialchars($chartConfig['localTimeLabel'], ENT_QUOTES, 'UTF-8') ?>, <?= htmlspecialchars($chartConfig['localTimeLabel'], ENT_QUOTES, 'UTF-8') ?>,
<?= htmlspecialchars($chartLocationName, ENT_QUOTES, 'UTF-8') ?> <?= htmlspecialchars($chartLocationName, ENT_QUOTES, 'UTF-8') ?>
@@ -567,25 +640,25 @@ $starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_U
</div> </div>
<div class="monatskarte-stage monatskarte-stage-south"> <div class="monatskarte-stage monatskarte-stage-south">
<canvas id="monatskarteSouthCanvas" width="1200" height="720" aria-label="Südhimmel-Projektion"></canvas> <canvas id="monatskarteSouthCanvas" width="1200" height="720" aria-label="Südhimmel-Projektion"></canvas>
</div> </div>
<div class="monatskarte-footer"> <div class="monatskarte-footer">
<p id="monatskarteSouthStatus">Bereite Südhimmelkarte vor...</p> <p id="monatskarteSouthStatus">Bereite Südhimmelkarte vor...</p>
</div> </div>
</section> </section>
<section class="monatskarte-card"> <section class="monatskarte-card">
<div class="monatskarte-header"> <div class="monatskarte-header">
<div> <div>
<h3>Mondphasen <?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?></h3> <h3>Ereignisse <?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?></h3>
<p>Chronologisch für <?= htmlspecialchars($chartTimezone, ENT_QUOTES, 'UTF-8') ?></p> <p>Chronologische Monatsliste fuer <?= htmlspecialchars($chartTimezone, ENT_QUOTES, 'UTF-8') ?></p>
</div> </div>
</div> </div>
<div class="monatskarte-footer"> <div class="monatskarte-footer">
<?php if ($monthEventList): ?> <?php if ($monthEventList): ?>
<div class="riseset-grid" style="grid-template-columns: 1.3fr 1fr 1fr; row-gap: 10px;"> <div class="riseset-grid" style="grid-template-columns: 1.5fr 1fr 1fr; row-gap: 10px;">
<span class="riseset-label riseset-dim">Ereignis</span> <span class="riseset-label riseset-dim">Ereignis</span>
<span class="riseset-label riseset-dim">Datum</span> <span class="riseset-label riseset-dim">Datum</span>
<span class="riseset-label riseset-dim">Uhrzeit</span> <span class="riseset-label riseset-dim">Uhrzeit</span>
@@ -596,68 +669,11 @@ $starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_U
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<p>Keine Mondphasen für diesen Monat gefunden.</p> <p>Fuer diesen Monat wurden keine Ereignisse gefunden.</p>
<?php endif; ?> <?php endif; ?>
</div> </div>
</section> </section>
<section class="monatskarte-card">
<div class="monatskarte-header">
<div>
<h3>Mond nahe Planeten</h3>
<p>Maximalabstand <?= htmlspecialchars(number_format($moonPlanetMaxSeparationDeg, 1, ',', ''), ENT_QUOTES, 'UTF-8') ?>°</p>
</div>
</div>
<div class="monatskarte-footer">
<?php if ($moonPlanetApproaches): ?>
<div class="riseset-grid" style="grid-template-columns: 1.5fr 1fr 1fr 1fr; row-gap: 10px;">
<span class="riseset-label riseset-dim">Ereignis</span>
<span class="riseset-label riseset-dim">Datum</span>
<span class="riseset-label riseset-dim">Uhrzeit</span>
<span class="riseset-label riseset-dim">Abstand</span>
<?php foreach ($moonPlanetApproaches as $approachEvent): ?>
<span class="riseset-val"><?= htmlspecialchars((string) ($approachEvent['label'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars((string) ($approachEvent['local_date'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars((string) ($approachEvent['local_time'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars(number_format((float) ($approachEvent['separation_deg'] ?? 0), 2, ',', ''), ENT_QUOTES, 'UTF-8') ?>°</span>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>Keine Mond-Planet-Nähen innerhalb von <?= htmlspecialchars(number_format($moonPlanetMaxSeparationDeg, 1, ',', ''), ENT_QUOTES, 'UTF-8') ?>° gefunden.</p>
<?php endif; ?>
</div>
</section>
<section class="monatskarte-card">
<div class="monatskarte-header">
<div>
<h3>Sternbedeckungen</h3>
<p>Bis Grenzgr&ouml;&szlig;e <?= htmlspecialchars(number_format($moonOccultationMaxMag, 1, ',', ''), ENT_QUOTES, 'UTF-8') ?></p>
</div>
</div>
<div class="monatskarte-footer">
<?php if ($moonOccultations): ?>
<div class="riseset-grid" style="grid-template-columns: 1.6fr 1fr 1fr 0.9fr; row-gap: 10px;">
<span class="riseset-label riseset-dim">Stern</span>
<span class="riseset-label riseset-dim">Datum</span>
<span class="riseset-label riseset-dim">Uhrzeit</span>
<span class="riseset-label riseset-dim">Mag</span>
<?php foreach ($moonOccultations as $occultationEvent): ?>
<?php $star = is_array($occultationEvent['star'] ?? null) ? $occultationEvent['star'] : []; ?>
<?php $maximum = is_array($occultationEvent['maximum'] ?? null) ? $occultationEvent['maximum'] : []; ?>
<span class="riseset-val"><?= htmlspecialchars((string) ($star['label'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars((string) ($maximum['local_date'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars((string) ($maximum['local_time'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
<span class="riseset-val"><?= htmlspecialchars(number_format((float) ($star['mag'] ?? 0), 1, ',', ''), ENT_QUOTES, 'UTF-8') ?></span>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>Keine Sternbedeckungen bis Magnitude <?= htmlspecialchars(number_format($moonOccultationMaxMag, 1, ',', ''), ENT_QUOTES, 'UTF-8') ?> gefunden.</p>
<?php endif; ?>
</div>
</section>
</div> </div>
</section> </section>
@@ -1265,18 +1281,18 @@ const MONTH_CHART_ERROR = <?= $starDataErrorJson ?: 'null' ?>;
drawDirectionLabel(0, 'Norden', 18, '600 13px Rajdhani, sans-serif', 0.78); drawDirectionLabel(0, 'Norden', 18, '600 13px Rajdhani, sans-serif', 0.78);
drawDirectionLabel(90, 'Westen', 22, '600 13px Rajdhani, sans-serif', 0.78); drawDirectionLabel(90, 'Westen', 22, '600 13px Rajdhani, sans-serif', 0.78);
drawDirectionLabel(180, 'Süden', 18, '600 13px Rajdhani, sans-serif', 0.78); drawDirectionLabel(180, 'Süden', 18, '600 13px Rajdhani, sans-serif', 0.78);
drawDirectionLabel(270, 'Osten', 22, '600 13px Rajdhani, sans-serif', 0.78); drawDirectionLabel(270, 'Osten', 22, '600 13px Rajdhani, sans-serif', 0.78);
drawDirectionLabel(45, 'Nordosten', 18, '600 11px Rajdhani, sans-serif', 0.64); drawDirectionLabel(45, 'Nordosten', 18, '600 11px Rajdhani, sans-serif', 0.64);
drawDirectionLabel(315, 'Nordwesten', 18, '600 11px Rajdhani, sans-serif', 0.64); drawDirectionLabel(315, 'Nordwesten', 18, '600 11px Rajdhani, sans-serif', 0.64);
drawDirectionLabel(135, 'Südosten', 18, '600 11px Rajdhani, sans-serif', 0.64); drawDirectionLabel(135, 'Südosten', 18, '600 11px Rajdhani, sans-serif', 0.64);
drawDirectionLabel(225, 'Südwesten', 18, '600 11px Rajdhani, sans-serif', 0.64); drawDirectionLabel(225, 'Südwesten', 18, '600 11px Rajdhani, sans-serif', 0.64);
ctx.fillStyle = 'rgba(214, 226, 255, 0.62)'; ctx.fillStyle = 'rgba(214, 226, 255, 0.62)';
ctx.font = '600 11px Rajdhani, sans-serif'; ctx.font = '600 11px Rajdhani, sans-serif';
ctx.fillText('60°', cx, cy - radius * 0.66 - 6); ctx.fillText('60°', cx, cy - radius * 0.66 - 6);
ctx.fillText('30°', cx, cy - radius * 0.33 - 6); ctx.fillText('30°', cx, cy - radius * 0.33 - 6);
ctx.fillText('Zenit', cx, cy); ctx.fillText('Zenit', cx, cy);
} }
@@ -2356,9 +2372,9 @@ const MONTH_CHART_ERROR = <?= $starDataErrorJson ?: 'null' ?>;
const directionLabels = [ const directionLabels = [
{ az: 90, text: 'Osten' }, { az: 90, text: 'Osten' },
{ az: 135, text: 'Südosten' }, { az: 135, text: 'Südosten' },
{ az: 180, text: 'Süden' }, { az: 180, text: 'Süden' },
{ az: 225, text: 'Südwesten' }, { az: 225, text: 'Südwesten' },
{ az: 270, text: 'Westen' }, { az: 270, text: 'Westen' },
]; ];
@@ -2384,7 +2400,7 @@ const MONTH_CHART_ERROR = <?= $starDataErrorJson ?: 'null' ?>;
ctx.font = '600 11px Rajdhani, sans-serif'; ctx.font = '600 11px Rajdhani, sans-serif';
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.textBaseline = 'middle'; ctx.textBaseline = 'middle';
ctx.fillText(`${alt}°`, pt.x + 8, pt.y); ctx.fillText(`${alt}°`, pt.x + 8, pt.y);
} }
} }
@@ -2569,7 +2585,7 @@ const MONTH_CHART_ERROR = <?= $starDataErrorJson ?: 'null' ?>;
drawMoon(width, height); drawMoon(width, height);
drawPlanets(width, height); drawPlanets(width, height);
drawStars(width, height); drawStars(width, height);
statusEl.textContent = 'Südhimmel für ' + MONTH_CHART_CONFIG.localTimeLabel; statusEl.textContent = 'Südhimmel für ' + MONTH_CHART_CONFIG.localTimeLabel;
} }
resizeCanvas(); resizeCanvas();
Binary file not shown.
+226 -2
View File
@@ -466,6 +466,66 @@ def action_moon_phases_for_month(args: list[str]) -> dict:
} }
def action_season_changes_for_month(args: list[str]) -> dict:
if len(args) != 3:
fail(
"Aktion season_changes_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)})
season_info = astronomy.Seasons(year)
season_defs = [
("Fruehlingsanfang", season_info.mar_equinox, "march_equinox"),
("Sommeranfang", season_info.jun_solstice, "june_solstice"),
("Herbstanfang", season_info.sep_equinox, "september_equinox"),
("Winteranfang", season_info.dec_solstice, "december_solstice"),
]
changes = []
for label, time_value, key in season_defs:
dt_utc = time_to_datetime(time_value)
local_dt = dt_utc.astimezone(tz)
if local_dt.year != year or local_dt.month != month:
continue
changes.append({
"key": key,
"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"),
})
changes.sort(key=lambda item: item["local_iso"])
return {
"ok": True,
"action": "season_changes_for_month",
"selected": {
"year": year,
"month": month,
"timezone": timezone_name,
},
"changes": changes,
}
def moon_planet_separation_deg( def moon_planet_separation_deg(
body: astronomy.Body, body: astronomy.Body,
observer: astronomy.Observer, observer: astronomy.Observer,
@@ -477,6 +537,18 @@ def moon_planet_separation_deg(
return spherical_separation_deg(float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec)) return spherical_separation_deg(float(moon_eq.ra), float(moon_eq.dec), float(body_eq.ra), float(body_eq.dec))
def planet_pair_separation_deg(
body_a: astronomy.Body,
body_b: astronomy.Body,
observer: astronomy.Observer,
dt_utc: datetime,
) -> float:
time_value = dt_to_time(dt_utc)
eq_a = astronomy.Equator(body_a, time_value, observer, True, True)
eq_b = astronomy.Equator(body_b, time_value, observer, True, True)
return spherical_separation_deg(float(eq_a.ra), float(eq_a.dec), float(eq_b.ra), float(eq_b.dec))
def refine_minimum_separation( def refine_minimum_separation(
body: astronomy.Body, body: astronomy.Body,
observer: astronomy.Observer, observer: astronomy.Observer,
@@ -501,6 +573,31 @@ def refine_minimum_separation(
return best, moon_planet_separation_deg(body, observer, best) return best, moon_planet_separation_deg(body, observer, best)
def refine_planet_pair_minimum_separation(
body_a: astronomy.Body,
body_b: 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 = planet_pair_separation_deg(body_a, body_b, observer, m1)
f2 = planet_pair_separation_deg(body_a, body_b, observer, m2)
if f1 <= f2:
right = m2
else:
left = m1
best = left + (right - left) / 2
return best, planet_pair_separation_deg(body_a, body_b, observer, best)
def action_moon_planet_approaches(args: list[str]) -> dict: def action_moon_planet_approaches(args: list[str]) -> dict:
if len(args) != 7: if len(args) != 7:
fail( fail(
@@ -612,6 +709,123 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
} }
def action_planet_conjunctions_for_month(args: list[str]) -> dict:
if len(args) != 7:
fail(
"Aktion planet_conjunctions_for_month 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"),
]
conjunctions = []
for left_index in range(len(planet_defs) - 1):
left_label, left_body, left_key = planet_defs[left_index]
for right_index in range(left_index + 1, len(planet_defs)):
right_label, right_body, right_key = planet_defs[right_index]
samples: list[tuple[datetime, float]] = []
current = utc_start
while current <= utc_end:
samples.append((current, planet_pair_separation_deg(left_body, right_body, observer, current)))
current += scan_step
if samples[-1][0] < utc_end:
samples.append((utc_end, planet_pair_separation_deg(left_body, right_body, observer, utc_end)))
seen_ranges: list[tuple[datetime, datetime]] = []
for index in range(1, len(samples) - 1):
curr_t, curr_sep = samples[index]
prev_sep = samples[index - 1][1]
next_sep = samples[index + 1][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_planet_pair_minimum_separation(left_body, right_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))
conjunctions.append({
"planet_a_key": left_key,
"planet_a_label": left_label,
"planet_b_key": right_key,
"planet_b_label": right_label,
"label": f"{left_label} nahe {right_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"),
})
conjunctions.sort(key=lambda item: item["local_iso"])
return {
"ok": True,
"action": "planet_conjunctions_for_month",
"selected": {
"year": year,
"month": month,
"timezone": timezone_name,
"max_separation_deg": max_sep_deg,
},
"conjunctions": conjunctions,
}
def action_moon_star_occultations_for_month(args: list[str]) -> dict: def action_moon_star_occultations_for_month(args: list[str]) -> dict:
if len(args) != 7: if len(args) != 7:
fail( fail(
@@ -1668,7 +1882,7 @@ def action_satellite_passes(args: list[str]) -> dict:
def main() -> None: def main() -> None:
if len(sys.argv) < 2: 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", "moon_planet_approaches", "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", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "moon_star_occultations_for_month"]})
action = sys.argv[1] action = sys.argv[1]
args = sys.argv[2:] args = sys.argv[2:]
@@ -1683,6 +1897,11 @@ def main() -> None:
print(json.dumps(result, ensure_ascii=False)) print(json.dumps(result, ensure_ascii=False))
return return
if action == "season_changes_for_month":
result = action_season_changes_for_month(args)
print(json.dumps(result, ensure_ascii=False))
return
if action == "moon_phase_details": if action == "moon_phase_details":
result = action_moon_phase_details(args) result = action_moon_phase_details(args)
print(json.dumps(result, ensure_ascii=False)) print(json.dumps(result, ensure_ascii=False))
@@ -1713,12 +1932,17 @@ def main() -> None:
print(json.dumps(result, ensure_ascii=False)) print(json.dumps(result, ensure_ascii=False))
return return
if action == "planet_conjunctions_for_month":
result = action_planet_conjunctions_for_month(args)
print(json.dumps(result, ensure_ascii=False))
return
if action == "moon_star_occultations_for_month": if action == "moon_star_occultations_for_month":
result = action_moon_star_occultations_for_month(args) result = action_moon_star_occultations_for_month(args)
print(json.dumps(result, ensure_ascii=False)) print(json.dumps(result, ensure_ascii=False))
return 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"]}) 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", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "moon_star_occultations_for_month"]})
if __name__ == "__main__": if __name__ == "__main__":
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.