Ephemeriden berechnet nun auch Kleinplaneten und Kometen

This commit is contained in:
2026-04-28 09:19:51 +02:00
parent c59e3e7f2e
commit 5adca790eb
2 changed files with 674 additions and 77 deletions
+231 -8
View File
@@ -15,6 +15,82 @@ $headerIntroSub = '';
$loggedIn = isset($_SESSION['user_id']);
$defaultLocation = null;
$favoriteMinorplanetOptions = [];
$favoriteCometOptions = [];
$minorplanetPayloads = [];
$cometPayloads = [];
function decodeMpcPackedDateChar(string $value): ?int
{
if ($value >= '0' && $value <= '9') {
return (int) $value;
}
$upper = strtoupper($value);
if ($upper >= 'A' && $upper <= 'V') {
return ord($upper) - ord('A') + 10;
}
return null;
}
function decodeMpcPackedDate(?string $packed): ?string
{
if ($packed === null || strlen($packed) !== 5) {
return null;
}
$centuryMap = [
'I' => 1800,
'J' => 1900,
'K' => 2000,
];
$centuryCode = strtoupper($packed[0]);
if (!isset($centuryMap[$centuryCode])) {
return null;
}
$yearPart = substr($packed, 1, 2);
if (!ctype_digit($yearPart)) {
return null;
}
$month = decodeMpcPackedDateChar($packed[3]);
$day = decodeMpcPackedDateChar($packed[4]);
if ($month === null || $day === null || $month < 1 || $month > 12 || $day < 1 || $day > 31) {
return null;
}
return sprintf('%04d-%02d-%02d', $centuryMap[$centuryCode] + (int) $yearPart, $month, $day);
}
function buildCometPerihelionIso(array $comet): ?string
{
$year = isset($comet['year_of_perihelion']) ? (int) $comet['year_of_perihelion'] : 0;
$month = isset($comet['month_of_perihelion']) ? (int) $comet['month_of_perihelion'] : 0;
$dayValue = isset($comet['day_of_perihelion']) ? (float) $comet['day_of_perihelion'] : 0.0;
if ($year <= 0 || $month < 1 || $month > 12 || $dayValue <= 0.0) {
return null;
}
$day = (int) floor($dayValue);
if ($day < 1 || $day > 31) {
return null;
}
$fractionalDay = $dayValue - $day;
$seconds = (int) round($fractionalDay * 86400.0);
try {
$date = new DateTimeImmutable(sprintf('%04d-%02d-%02d 00:00:00', $year, $month, $day), new DateTimeZone('UTC'));
} catch (Throwable $e) {
return null;
}
return $date->modify(sprintf('+%d seconds', $seconds))->format('c');
}
if ($loggedIn && isset($_SESSION['user_id'])) {
try {
@@ -37,8 +113,104 @@ if ($loggedIn && isset($_SESSION['user_id'])) {
);
$stmtLoc->execute([(int) $_SESSION['user_id']]);
$defaultLocation = $stmtLoc->fetch() ?: null;
$stmtFavoriteMinorplanets = $pdo->prepare(
'SELECT
mp.id,
mp.mp_number,
mp.designation_text,
mp.desig_packed,
mp.epoch_packed,
mp.mean_anomaly_deg,
mp.arg_perihelion_deg,
mp.ascending_node_deg,
mp.inclination_deg,
mp.eccentricity,
mp.mean_motion_deg_per_day,
mp.semimajor_axis_au
FROM app_user_minorplanets aum
INNER JOIN minorplanets_mpc mp
ON mp.id = aum.minorplanet_id
WHERE aum.user_id = ?
ORDER BY aum.is_favorite DESC, mp.mp_number IS NULL ASC, mp.mp_number ASC, mp.designation_text ASC'
);
$stmtFavoriteMinorplanets->execute([(int) $_SESSION['user_id']]);
foreach ($stmtFavoriteMinorplanets->fetchAll() as $minorplanet) {
$minorplanetLabel = trim((string) ($minorplanet['designation_text'] ?? ''));
if ($minorplanetLabel === '') {
$minorplanetLabel = trim((string) ($minorplanet['desig_packed'] ?? ''));
}
if ($minorplanetLabel === '') {
continue;
}
if (
$minorplanet['mp_number'] !== null
&& !str_contains($minorplanetLabel, '(' . (string) $minorplanet['mp_number'] . ')')
) {
$minorplanetLabel = '(' . (string) $minorplanet['mp_number'] . ') ' . $minorplanetLabel;
}
$bodyKey = 'minorplanet:' . (string) $minorplanet['id'];
$favoriteMinorplanetOptions[$bodyKey] = $minorplanetLabel;
$minorplanetPayloads[$bodyKey] = [
'id' => (int) $minorplanet['id'],
'designation' => $minorplanetLabel,
'epochDateIso' => decodeMpcPackedDate((string) ($minorplanet['epoch_packed'] ?? '')),
'meanAnomalyDeg' => isset($minorplanet['mean_anomaly_deg']) ? (float) $minorplanet['mean_anomaly_deg'] : null,
'argPerihelionDeg' => isset($minorplanet['arg_perihelion_deg']) ? (float) $minorplanet['arg_perihelion_deg'] : null,
'ascendingNodeDeg' => isset($minorplanet['ascending_node_deg']) ? (float) $minorplanet['ascending_node_deg'] : null,
'inclinationDeg' => isset($minorplanet['inclination_deg']) ? (float) $minorplanet['inclination_deg'] : null,
'eccentricity' => isset($minorplanet['eccentricity']) ? (float) $minorplanet['eccentricity'] : null,
'meanMotionDegPerDay' => isset($minorplanet['mean_motion_deg_per_day']) ? (float) $minorplanet['mean_motion_deg_per_day'] : null,
'semimajorAxisAu' => isset($minorplanet['semimajor_axis_au']) ? (float) $minorplanet['semimajor_axis_au'] : null,
];
}
$stmtFavoriteComets = $pdo->prepare(
'SELECT
c.id,
c.designation_and_name,
c.year_of_perihelion,
c.month_of_perihelion,
c.day_of_perihelion,
c.perihelion_dist_au,
c.eccentricity,
c.arg_perihelion_deg,
c.ascending_node_deg,
c.inclination_deg
FROM app_user_comets auc
INNER JOIN comets_mpc c
ON c.id = auc.comet_id
WHERE auc.user_id = ?
ORDER BY auc.is_favorite DESC, c.designation_and_name ASC'
);
$stmtFavoriteComets->execute([(int) $_SESSION['user_id']]);
foreach ($stmtFavoriteComets->fetchAll() as $comet) {
$cometLabel = trim((string) ($comet['designation_and_name'] ?? ''));
if ($cometLabel === '') {
continue;
}
$bodyKey = 'comet:' . (string) $comet['id'];
$favoriteCometOptions[$bodyKey] = $cometLabel;
$cometPayloads[$bodyKey] = [
'id' => (int) $comet['id'],
'designation' => $cometLabel,
'perihelionIso' => buildCometPerihelionIso($comet),
'perihelionDistanceAu' => isset($comet['perihelion_dist_au']) ? (float) $comet['perihelion_dist_au'] : null,
'eccentricity' => isset($comet['eccentricity']) ? (float) $comet['eccentricity'] : null,
'argPerihelionDeg' => isset($comet['arg_perihelion_deg']) ? (float) $comet['arg_perihelion_deg'] : null,
'ascendingNodeDeg' => isset($comet['ascending_node_deg']) ? (float) $comet['ascending_node_deg'] : null,
'inclinationDeg' => isset($comet['inclination_deg']) ? (float) $comet['inclination_deg'] : null,
];
}
} catch (Throwable $e) {
$defaultLocation = null;
$favoriteMinorplanetOptions = [];
$favoriteCometOptions = [];
$minorplanetPayloads = [];
$cometPayloads = [];
}
}
@@ -54,6 +226,8 @@ $planetOptions = [
'Neptune' => 'Neptun',
];
$bodyOptions = $planetOptions + $favoriteMinorplanetOptions + $favoriteCometOptions;
$intervalOptions = [
'1' => '1 Minute',
'10' => '10 Minuten',
@@ -77,7 +251,7 @@ $rangeUnitOptions = [
];
$selectedBody = (string) ($_POST['body'] ?? 'Mercury');
if (!isset($planetOptions[$selectedBody])) {
if (!isset($bodyOptions[$selectedBody])) {
$selectedBody = 'Mercury';
}
@@ -174,10 +348,13 @@ $exportFormat = (string) ($_POST['export_format'] ?? '');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($pageLocation === null || $pageLocation['latitude'] === null || $pageLocation['longitude'] === null) {
$errorMessage = 'Kein gültiger Standard-Standort verfügbar. Bitte zuerst in den Einstellungen einen Standard-Standort setzen.';
} elseif (false && (str_starts_with($selectedBody, 'comet:') || str_starts_with($selectedBody, 'minorplanet:'))) {
$errorMessage = 'Favoriten-Kometen und Favoriten-Kleinplaneten sind jetzt in der Auswahl sichtbar. Die eigentliche Ephemeriden-Berechnung dafür binden wir als Nächstes an.';
} elseif (!ctype_digit($rangeValueInput) || (int) $rangeValueInput < 1 || (int) $rangeValueInput > 31) {
$errorMessage = 'Der Zeitraum muss als ganze Zahl zwischen 1 und 31 angegeben werden.';
} else {
$pythonResult = runEphemeridenPythonApi('planet_ephemeris', [
$pythonAction = 'planet_ephemeris';
$pythonArgs = [
(string) $pageLocation['latitude'],
(string) $pageLocation['longitude'],
(string) $pageLocation['elevation'],
@@ -186,7 +363,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$selectedInterval,
$rangeValueInput,
$selectedRangeUnit,
]);
];
if (isset($minorplanetPayloads[$selectedBody])) {
$pythonAction = 'small_body_ephemeris';
$pythonArgs = [
(string) $pageLocation['latitude'],
(string) $pageLocation['longitude'],
(string) $pageLocation['elevation'],
(string) $pageLocation['timezone'],
'minorplanet',
base64_encode(json_encode($minorplanetPayloads[$selectedBody], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'),
$selectedInterval,
$rangeValueInput,
$selectedRangeUnit,
];
} elseif (isset($cometPayloads[$selectedBody])) {
$pythonAction = 'small_body_ephemeris';
$pythonArgs = [
(string) $pageLocation['latitude'],
(string) $pageLocation['longitude'],
(string) $pageLocation['elevation'],
(string) $pageLocation['timezone'],
'comet',
base64_encode(json_encode($cometPayloads[$selectedBody], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}'),
$selectedInterval,
$rangeValueInput,
$selectedRangeUnit,
];
}
$pythonResult = runEphemeridenPythonApi($pythonAction, $pythonArgs);
if (!$pythonResult['ok']) {
$errorMessage = (string) (
@@ -201,7 +408,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($exportFormat === 'csv' && $ephemerisRows !== []) {
$filename = 'ephemeriden-' . ephemeriden_slug((string) ($planetOptions[$selectedBody] ?? 'objekt')) . '.csv';
$filename = 'ephemeriden-' . ephemeriden_slug((string) ($bodyOptions[$selectedBody] ?? 'objekt')) . '.csv';
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
@@ -335,11 +542,27 @@ require __DIR__ . '/header.php';
<form method="post" class="ephemeriden-form">
<div class="ephemeriden-fields-row">
<div class="form-group">
<label for="ephemeridenBody">Planet</label>
<label for="ephemeridenBody">Himmelskörper</label>
<select id="ephemeridenBody" name="body">
<?php foreach ($planetOptions as $bodyValue => $bodyLabel): ?>
<option value="<?= ephemeriden_h($bodyValue) ?>"<?= $bodyValue === $selectedBody ? ' selected' : '' ?>><?= ephemeriden_h($bodyLabel) ?></option>
<?php endforeach; ?>
<optgroup label="Sonne, Mond und Planeten">
<?php foreach ($planetOptions as $bodyValue => $bodyLabel): ?>
<option value="<?= ephemeriden_h($bodyValue) ?>"<?= $bodyValue === $selectedBody ? ' selected' : '' ?>><?= ephemeriden_h($bodyLabel) ?></option>
<?php endforeach; ?>
</optgroup>
<?php if ($favoriteMinorplanetOptions !== []): ?>
<optgroup label="Meine Kleinplaneten">
<?php foreach ($favoriteMinorplanetOptions as $bodyValue => $bodyLabel): ?>
<option value="<?= ephemeriden_h($bodyValue) ?>"<?= $bodyValue === $selectedBody ? ' selected' : '' ?>><?= ephemeriden_h($bodyLabel) ?></option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
<?php if ($favoriteCometOptions !== []): ?>
<optgroup label="Meine Kometen">
<?php foreach ($favoriteCometOptions as $bodyValue => $bodyLabel): ?>
<option value="<?= ephemeriden_h($bodyValue) ?>"<?= $bodyValue === $selectedBody ? ' selected' : '' ?>><?= ephemeriden_h($bodyLabel) ?></option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
</select>
</div>