709 lines
25 KiB
PHP
709 lines
25 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
ini_set('display_errors', '1');
|
|
error_reporting(E_ALL);
|
|
session_start();
|
|
|
|
$loggedIn = isset($_SESSION['user_id']);
|
|
$pageTitle = 'Kometen - AstroTools';
|
|
$headerIntroPre = 'Himmel & Planeten';
|
|
$headerIntroTitle = 'Kometen';
|
|
$headerIntroSub = 'Interessante Kometen aus der MPC-Datenbank mit Fokus auf bevorstehende und aktuelle Periheldurchgaenge.';
|
|
|
|
function h(?string $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function runPythonApi(string $action, array $args): array
|
|
{
|
|
$scriptPath = __DIR__ . '/py/api.py';
|
|
$pythonCandidates = PHP_OS_FAMILY === 'Windows'
|
|
? ['python', 'py']
|
|
: ['/usr/bin/python3', '/usr/bin/python', 'python3', 'python'];
|
|
|
|
$escapedArguments = array_map('escapeshellarg', array_merge([$scriptPath, $action], $args));
|
|
$lastError = 'Python konnte nicht gestartet werden.';
|
|
|
|
foreach ($pythonCandidates as $pythonBinary) {
|
|
$command = $pythonBinary . ' ' . implode(' ', $escapedArguments) . ' 2>&1';
|
|
$output = [];
|
|
$resultCode = 0;
|
|
exec($command, $output, $resultCode);
|
|
|
|
$joined = trim(implode("\n", $output));
|
|
$decoded = json_decode($joined, true);
|
|
|
|
if (is_array($decoded)) {
|
|
return [
|
|
'ok' => (bool) ($decoded['ok'] ?? false),
|
|
'data' => $decoded,
|
|
'raw' => $joined,
|
|
'command' => $command,
|
|
];
|
|
}
|
|
|
|
if ($joined !== '') {
|
|
$lastError = $joined;
|
|
} elseif ($resultCode !== 0) {
|
|
$lastError = 'Fehlercode ' . $resultCode . ' bei ' . $pythonBinary;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'ok' => false,
|
|
'error' => $lastError,
|
|
];
|
|
}
|
|
|
|
function runPythonApiWithJsonPayloadFile(string $action, array $payload): array
|
|
{
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'skyview_py_');
|
|
if ($tempFile === false) {
|
|
return [
|
|
'ok' => false,
|
|
'error' => 'Temporäre Datei für Python-Payload konnte nicht erstellt werden.',
|
|
];
|
|
}
|
|
|
|
$jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if ($jsonPayload === false) {
|
|
@unlink($tempFile);
|
|
return [
|
|
'ok' => false,
|
|
'error' => 'Python-Payload konnte nicht als JSON kodiert werden.',
|
|
];
|
|
}
|
|
|
|
if (file_put_contents($tempFile, $jsonPayload) === false) {
|
|
@unlink($tempFile);
|
|
return [
|
|
'ok' => false,
|
|
'error' => 'Temporäre Datei für Python-Payload konnte nicht geschrieben werden.',
|
|
];
|
|
}
|
|
|
|
try {
|
|
return runPythonApi($action, ['@' . $tempFile]);
|
|
} finally {
|
|
@unlink($tempFile);
|
|
}
|
|
}
|
|
|
|
function formatDecimalGerman(?float $value, int $decimals = 3): string
|
|
{
|
|
if ($value === null || !is_finite($value)) {
|
|
return '-';
|
|
}
|
|
|
|
return number_format($value, $decimals, ',', '.');
|
|
}
|
|
|
|
function formatRaHours(?float $hours): string
|
|
{
|
|
if ($hours === null || !is_finite($hours)) {
|
|
return '-';
|
|
}
|
|
|
|
$normalized = fmod($hours, 24.0);
|
|
if ($normalized < 0.0) {
|
|
$normalized += 24.0;
|
|
}
|
|
|
|
$totalSeconds = (int) round($normalized * 3600.0);
|
|
$raHours = intdiv($totalSeconds, 3600) % 24;
|
|
$raMinutes = intdiv($totalSeconds % 3600, 60);
|
|
$raSeconds = $totalSeconds % 60;
|
|
|
|
return sprintf('%02dh %02dm %02ds', $raHours, $raMinutes, $raSeconds);
|
|
}
|
|
|
|
function formatDeclination(?float $degrees): string
|
|
{
|
|
if ($degrees === null || !is_finite($degrees)) {
|
|
return '-';
|
|
}
|
|
|
|
$sign = $degrees < 0.0 ? '-' : '+';
|
|
$absolute = abs($degrees);
|
|
$deg = (int) floor($absolute);
|
|
$minutesFloat = ($absolute - $deg) * 60.0;
|
|
$minutes = (int) floor($minutesFloat);
|
|
$seconds = (int) round(($minutesFloat - $minutes) * 60.0);
|
|
|
|
if ($seconds >= 60) {
|
|
$seconds -= 60;
|
|
$minutes += 1;
|
|
}
|
|
|
|
if ($minutes >= 60) {
|
|
$minutes -= 60;
|
|
$deg += 1;
|
|
}
|
|
|
|
return sprintf('%s%02d° %02d\' %02d"', $sign, $deg, $minutes, $seconds);
|
|
}
|
|
|
|
function buildPerihelionDate(array $row): ?DateTimeImmutable
|
|
{
|
|
$year = isset($row['year_of_perihelion']) ? (int) $row['year_of_perihelion'] : 0;
|
|
$month = isset($row['month_of_perihelion']) ? (int) $row['month_of_perihelion'] : 0;
|
|
$dayValue = isset($row['day_of_perihelion']) ? (float) $row['day_of_perihelion'] : 0.0;
|
|
|
|
if ($year === 0 || $month < 1 || $month > 12 || $dayValue <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$day = (int) floor($dayValue);
|
|
if ($day < 1 || $day > 31) {
|
|
return null;
|
|
}
|
|
|
|
$fractionalDay = $dayValue - $day;
|
|
$seconds = (int) round($fractionalDay * 86400);
|
|
|
|
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));
|
|
}
|
|
|
|
function classifyOrbitType(string $orbitType): string
|
|
{
|
|
return match (strtoupper(trim($orbitType))) {
|
|
'P' => 'Periodisch',
|
|
'C' => 'Nicht periodisch',
|
|
'I' => 'Interstellar',
|
|
'D' => 'Verschollen / zerfallen',
|
|
'A' => 'Asteroidal',
|
|
default => trim($orbitType) !== '' ? trim($orbitType) : '-',
|
|
};
|
|
}
|
|
|
|
$config = require __DIR__ . '/../config/database.php';
|
|
$dsn = sprintf(
|
|
'mysql:host=%s;dbname=%s;charset=%s',
|
|
$config['host'],
|
|
$config['dbname'],
|
|
$config['charset'] ?? 'utf8mb4'
|
|
);
|
|
|
|
$pdo = new PDO(
|
|
$dsn,
|
|
$config['user'],
|
|
$config['pass'],
|
|
[
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
]
|
|
);
|
|
|
|
$userId = isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : 0;
|
|
$today = new DateTimeImmutable('today', new DateTimeZone('UTC'));
|
|
$windowStart = $today->modify('-180 days');
|
|
$windowEnd = $today->modify('+365 days');
|
|
$queryYearMin = (int) $windowStart->format('Y') - 1;
|
|
$queryYearMax = (int) $windowEnd->format('Y') + 1;
|
|
|
|
$stmtComets = $pdo->prepare("
|
|
SELECT
|
|
c.*,
|
|
CASE WHEN auc.`comet_id` IS NULL THEN 0 ELSE 1 END AS `is_tracked`,
|
|
CASE WHEN auc.`is_favorite` = 1 THEN 1 ELSE 0 END AS `is_favorite`
|
|
FROM `comets_mpc` c
|
|
LEFT JOIN `app_user_comets` auc
|
|
ON auc.`comet_id` = c.`id`
|
|
AND auc.`user_id` = :user_id
|
|
WHERE c.`year_of_perihelion` BETWEEN :year_min AND :year_max
|
|
OR auc.`comet_id` IS NOT NULL
|
|
ORDER BY c.`year_of_perihelion` ASC, c.`month_of_perihelion` ASC, c.`day_of_perihelion` ASC, c.`designation_and_name` ASC
|
|
");
|
|
$stmtComets->execute([
|
|
':user_id' => $userId,
|
|
':year_min' => $queryYearMin,
|
|
':year_max' => $queryYearMax,
|
|
]);
|
|
$cometRows = $stmtComets->fetchAll();
|
|
|
|
$allComets = [];
|
|
$trackedComets = [];
|
|
$interestingComets = [];
|
|
|
|
foreach ($cometRows as $row) {
|
|
$perihelionDate = buildPerihelionDate($row);
|
|
$daysFromToday = $perihelionDate ? (int) floor(($perihelionDate->getTimestamp() - $today->getTimestamp()) / 86400) : null;
|
|
|
|
$row['perihelion_date_object'] = $perihelionDate;
|
|
$row['perihelion_date_display'] = $perihelionDate ? $perihelionDate->format('d.m.Y H:i') . ' UTC' : '-';
|
|
$row['days_from_today'] = $daysFromToday;
|
|
$row['orbit_type_label'] = classifyOrbitType((string) ($row['orbit_type'] ?? ''));
|
|
$row['absolute_magnitude_display'] = isset($row['absolute_magnitude_h']) && $row['absolute_magnitude_h'] !== null
|
|
? formatDecimalGerman((float) $row['absolute_magnitude_h'], 1)
|
|
: '-';
|
|
$row['photometric_index_display'] = isset($row['slope_parameter_g']) && $row['slope_parameter_g'] !== null
|
|
? formatDecimalGerman((float) $row['slope_parameter_g'], 1)
|
|
: '-';
|
|
$row['estimated_magnitude'] = null;
|
|
$row['estimated_magnitude_display'] = '-';
|
|
$row['brightness_error'] = null;
|
|
|
|
$allComets[] = $row;
|
|
|
|
if ((int) ($row['is_tracked'] ?? 0) === 1) {
|
|
$trackedComets[] = $row;
|
|
}
|
|
|
|
if ($perihelionDate !== null && $perihelionDate >= $windowStart && $perihelionDate <= $windowEnd) {
|
|
$interestingComets[] = $row;
|
|
}
|
|
}
|
|
|
|
usort($trackedComets, static function (array $left, array $right): int {
|
|
$favoriteCompare = ((int) $right['is_favorite']) <=> ((int) $left['is_favorite']);
|
|
if ($favoriteCompare !== 0) {
|
|
return $favoriteCompare;
|
|
}
|
|
|
|
return ((int) ($left['days_from_today'] ?? PHP_INT_MAX)) <=> ((int) ($right['days_from_today'] ?? PHP_INT_MAX));
|
|
});
|
|
|
|
usort($interestingComets, static function (array $left, array $right): int {
|
|
$leftDistance = abs((int) ($left['days_from_today'] ?? PHP_INT_MAX));
|
|
$rightDistance = abs((int) ($right['days_from_today'] ?? PHP_INT_MAX));
|
|
if ($leftDistance !== $rightDistance) {
|
|
return $leftDistance <=> $rightDistance;
|
|
}
|
|
|
|
return strcmp((string) $left['designation_and_name'], (string) $right['designation_and_name']);
|
|
});
|
|
|
|
$interestingComets = array_slice($interestingComets, 0, 24);
|
|
$visibleCometIds = [];
|
|
foreach (array_merge($trackedComets, $interestingComets) as $visibleComet) {
|
|
$visibleId = isset($visibleComet['id']) ? (int) $visibleComet['id'] : 0;
|
|
if ($visibleId > 0) {
|
|
$visibleCometIds[$visibleId] = true;
|
|
}
|
|
}
|
|
|
|
$calculationMoment = new DateTimeImmutable('now', new DateTimeZone('UTC'));
|
|
$brightnessByCometId = [];
|
|
$brightnessError = null;
|
|
|
|
if ($visibleCometIds !== []) {
|
|
$pythonPayload = [
|
|
'date' => $calculationMoment->format('Y-m-d\TH:i:s\Z'),
|
|
'comets' => array_values(array_map(static function (array $row): array {
|
|
return [
|
|
'id' => $row['id'] ?? null,
|
|
'year_of_perihelion' => $row['year_of_perihelion'] ?? null,
|
|
'month_of_perihelion' => $row['month_of_perihelion'] ?? null,
|
|
'day_of_perihelion' => $row['day_of_perihelion'] ?? null,
|
|
'perihelion_dist_au' => $row['perihelion_dist_au'] ?? null,
|
|
'eccentricity' => $row['eccentricity'] ?? null,
|
|
'arg_perihelion_deg' => $row['arg_perihelion_deg'] ?? null,
|
|
'ascending_node_deg' => $row['ascending_node_deg'] ?? null,
|
|
'inclination_deg' => $row['inclination_deg'] ?? null,
|
|
'absolute_magnitude_h' => $row['absolute_magnitude_h'] ?? null,
|
|
'slope_parameter_g' => $row['slope_parameter_g'] ?? null,
|
|
];
|
|
}, array_filter($cometRows, static function (array $row) use ($visibleCometIds): bool {
|
|
$rowId = isset($row['id']) ? (int) $row['id'] : 0;
|
|
return $rowId > 0 && isset($visibleCometIds[$rowId]);
|
|
}))),
|
|
];
|
|
|
|
$pythonResult = runPythonApiWithJsonPayloadFile('comet_brightnesses', $pythonPayload);
|
|
|
|
if (($pythonResult['ok'] ?? false) && isset($pythonResult['data']['results']) && is_array($pythonResult['data']['results'])) {
|
|
foreach ($pythonResult['data']['results'] as $result) {
|
|
$resultId = isset($result['id']) ? (int) $result['id'] : 0;
|
|
if ($resultId > 0) {
|
|
$brightnessByCometId[$resultId] = $result;
|
|
}
|
|
}
|
|
} else {
|
|
$brightnessError = (string) ($pythonResult['error'] ?? 'Python-Berechnung nicht verfuegbar.');
|
|
}
|
|
}
|
|
|
|
$applyBrightness = static function (array $row) use ($brightnessByCometId): array {
|
|
$brightnessData = isset($row['id']) ? ($brightnessByCometId[(int) $row['id']] ?? null) : null;
|
|
$estimatedMagnitude = isset($brightnessData['estimated_magnitude']) && is_numeric($brightnessData['estimated_magnitude'])
|
|
? (float) $brightnessData['estimated_magnitude']
|
|
: null;
|
|
$raHours = isset($brightnessData['ra_hours']) && is_numeric($brightnessData['ra_hours'])
|
|
? (float) $brightnessData['ra_hours']
|
|
: null;
|
|
$decDeg = isset($brightnessData['dec_deg']) && is_numeric($brightnessData['dec_deg'])
|
|
? (float) $brightnessData['dec_deg']
|
|
: null;
|
|
$heliocentricDistance = isset($brightnessData['heliocentric_distance_au']) && is_numeric($brightnessData['heliocentric_distance_au'])
|
|
? (float) $brightnessData['heliocentric_distance_au']
|
|
: null;
|
|
$geocentricDistance = isset($brightnessData['geocentric_distance_au']) && is_numeric($brightnessData['geocentric_distance_au'])
|
|
? (float) $brightnessData['geocentric_distance_au']
|
|
: null;
|
|
|
|
$row['estimated_magnitude'] = $estimatedMagnitude;
|
|
$row['estimated_magnitude_display'] = $estimatedMagnitude !== null
|
|
? formatDecimalGerman($estimatedMagnitude, 1)
|
|
: '-';
|
|
$row['ra_hours'] = $raHours;
|
|
$row['ra_display'] = formatRaHours($raHours);
|
|
$row['dec_deg'] = $decDeg;
|
|
$row['dec_display'] = formatDeclination($decDeg);
|
|
$row['heliocentric_distance_au'] = $heliocentricDistance;
|
|
$row['heliocentric_distance_display'] = formatDecimalGerman($heliocentricDistance, 3);
|
|
$row['geocentric_distance_au'] = $geocentricDistance;
|
|
$row['geocentric_distance_display'] = formatDecimalGerman($geocentricDistance, 3);
|
|
$row['brightness_error'] = is_array($brightnessData) && !empty($brightnessData['error'])
|
|
? (string) $brightnessData['error']
|
|
: null;
|
|
|
|
return $row;
|
|
};
|
|
|
|
$trackedComets = array_map($applyBrightness, $trackedComets);
|
|
$interestingComets = array_map($applyBrightness, $interestingComets);
|
|
$orbitTypeCounts = [];
|
|
foreach ($allComets as $comet) {
|
|
$orbitKey = (string) ($comet['orbit_type'] ?? '?');
|
|
$orbitTypeCounts[$orbitKey] = ($orbitTypeCounts[$orbitKey] ?? 0) + 1;
|
|
}
|
|
arsort($orbitTypeCounts);
|
|
?>
|
|
<?php require __DIR__ . '/header.php'; ?>
|
|
|
|
<style>
|
|
.comets-layout {
|
|
display: grid;
|
|
gap: 1.25rem;
|
|
}
|
|
|
|
.comets-intro {
|
|
color: #d9e7ff;
|
|
line-height: 1.65;
|
|
margin: 0;
|
|
}
|
|
|
|
.comets-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
gap: 0.9rem;
|
|
}
|
|
|
|
.comets-stat {
|
|
padding: 1rem 1.1rem;
|
|
border-radius: 1rem;
|
|
border: 1px solid rgba(130, 170, 255, 0.16);
|
|
background:
|
|
radial-gradient(circle at top left, rgba(82, 153, 255, 0.12), transparent 36%),
|
|
linear-gradient(180deg, rgba(13, 19, 33, 0.96), rgba(8, 11, 20, 0.98));
|
|
}
|
|
|
|
.comets-stat-label {
|
|
color: #9db2d4;
|
|
font-size: 0.82rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.comets-stat-value {
|
|
margin-top: 0.45rem;
|
|
color: #ffffff;
|
|
font-size: 1.8rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.comets-stat-help {
|
|
margin-top: 0.35rem;
|
|
color: #c7d6f4;
|
|
font-size: 0.92rem;
|
|
}
|
|
|
|
.comets-card {
|
|
padding: 1.2rem;
|
|
}
|
|
|
|
.comets-card h2 {
|
|
margin-bottom: 0.45rem;
|
|
}
|
|
|
|
.comets-card .hint {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.comets-chip-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.7rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.comets-chip {
|
|
padding: 0.45rem 0.7rem;
|
|
border-radius: 999px;
|
|
border: 1px solid rgba(130, 170, 255, 0.18);
|
|
background: rgba(255, 255, 255, 0.04);
|
|
color: #dce8ff;
|
|
font-size: 0.92rem;
|
|
}
|
|
|
|
.comets-table-wrap {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.comets-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.comets-table th,
|
|
.comets-table td {
|
|
padding: 0.78rem 0.8rem;
|
|
border-bottom: 1px solid rgba(130, 170, 255, 0.12);
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.comets-table th {
|
|
color: #a9bee0;
|
|
font-size: 0.84rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.comets-table td {
|
|
color: #eef4ff;
|
|
}
|
|
|
|
.comets-name {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.comets-meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.45rem;
|
|
margin-top: 0.35rem;
|
|
}
|
|
|
|
.comets-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
padding: 0.22rem 0.5rem;
|
|
border-radius: 999px;
|
|
font-size: 0.8rem;
|
|
color: #eef4ff;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
}
|
|
|
|
.comets-badge.is-favorite {
|
|
background: rgba(255, 208, 102, 0.12);
|
|
border-color: rgba(255, 208, 102, 0.28);
|
|
color: #ffd98b;
|
|
}
|
|
|
|
.comets-badge.is-tracked {
|
|
background: rgba(130, 170, 255, 0.12);
|
|
border-color: rgba(130, 170, 255, 0.26);
|
|
color: #bfd6ff;
|
|
}
|
|
|
|
.comets-days {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.comets-days.is-past {
|
|
color: #ffb2a8;
|
|
}
|
|
|
|
.comets-days.is-upcoming {
|
|
color: #b8f0c8;
|
|
}
|
|
|
|
@media (max-width: 960px) {
|
|
.comets-summary {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.comets-summary {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="comets-layout">
|
|
<section class="card comets-card">
|
|
<p class="comets-intro">
|
|
Diese erste Version liest die vorhandenen Kometen direkt aus <code>comets_mpc</code> und hebt Objekte hervor,
|
|
deren Perihel zwischen <strong><?= h($windowStart->format('d.m.Y')) ?></strong> und
|
|
<strong><?= h($windowEnd->format('d.m.Y')) ?></strong> liegt.
|
|
Nutzer-Favoriten aus <code>app_user_comets</code> werden zusaetzlich separat gezeigt.
|
|
</p>
|
|
<p class="hint">
|
|
Die Spalte <strong>m grob</strong> nutzt jetzt die uebliche MPC-Kometenformel fuer <strong>heute</strong>:
|
|
<code>H + 5 log(Delta) + 2,5 * n * log(r)</code>. Sie bleibt trotzdem nur eine Orientierung.
|
|
</p>
|
|
<p class="hint">
|
|
Position und Helligkeit werden fuer <strong><?= h($calculationMoment->format('d.m.Y H:i')) ?> UTC</strong> berechnet.
|
|
</p>
|
|
<?php if ($brightnessError !== null): ?>
|
|
<p class="hint">Die Python-Berechnung der Helligkeit war nicht verfuegbar: <?= h($brightnessError) ?></p>
|
|
<?php endif; ?>
|
|
|
|
<div class="comets-chip-row">
|
|
<?php foreach (array_slice($orbitTypeCounts, 0, 5, true) as $orbitType => $count): ?>
|
|
<span class="comets-chip"><?= h(classifyOrbitType((string) $orbitType)) ?>: <?= (int) $count ?></span>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="comets-summary">
|
|
<div class="comets-stat">
|
|
<div class="comets-stat-label">Kometen im Blickfenster</div>
|
|
<div class="comets-stat-value"><?= count($interestingComets) ?></div>
|
|
<div class="comets-stat-help">Perihel im aktuellen Zeitfenster</div>
|
|
</div>
|
|
<div class="comets-stat">
|
|
<div class="comets-stat-label">Meine Kometen</div>
|
|
<div class="comets-stat-value"><?= count($trackedComets) ?></div>
|
|
<div class="comets-stat-help">Aus den Favoriten uebernommen</div>
|
|
</div>
|
|
<div class="comets-stat">
|
|
<div class="comets-stat-label">Gesamt in Datenbank</div>
|
|
<div class="comets-stat-value"><?= count($allComets) ?></div>
|
|
<div class="comets-stat-help">Geladene Datensaetze im relevanten Jahresbereich</div>
|
|
</div>
|
|
<div class="comets-stat">
|
|
<div class="comets-stat-label">Zeitraum</div>
|
|
<div class="comets-stat-value"><?= h($today->format('Y')) ?></div>
|
|
<div class="comets-stat-help"><?= h($windowStart->format('d.m.Y')) ?> bis <?= h($windowEnd->format('d.m.Y')) ?></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card comets-card">
|
|
<h2>Meine Kometen</h2>
|
|
<?php if (empty($trackedComets)): ?>
|
|
<p class="hint">Du hast aktuell noch keine Kometen in deinen Favoriten. In <a href="my_favorites.php">Favoriten</a> kannst du welche markieren.</p>
|
|
<?php else: ?>
|
|
<div class="comets-table-wrap">
|
|
<table class="comets-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Komet</th>
|
|
<th>Typ</th>
|
|
<th>RA</th>
|
|
<th>Dec</th>
|
|
<th>Perihel</th>
|
|
<th>Abstand</th>
|
|
<th title="Grobe Helligkeitsschaetzung fuer heute nach H + 5 log(Delta) + 2,5 * n * log(r).">m grob</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($trackedComets as $comet): ?>
|
|
<?php
|
|
$daysClass = ((int) ($comet['days_from_today'] ?? 0)) < 0 ? 'is-past' : 'is-upcoming';
|
|
$daysLabel = $comet['days_from_today'] === null
|
|
? '-'
|
|
: ((int) $comet['days_from_today'] === 0
|
|
? 'Heute'
|
|
: (((int) $comet['days_from_today'] > 0 ? '+' : '') . (int) $comet['days_from_today'] . ' Tage'));
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<div class="comets-name"><?= h((string) $comet['designation_and_name']) ?></div>
|
|
<div class="comets-meta">
|
|
<?php if ((int) $comet['is_favorite'] === 1): ?>
|
|
<span class="comets-badge is-favorite">Favorit</span>
|
|
<?php endif; ?>
|
|
<span class="comets-badge is-tracked">Beobachtet</span>
|
|
<?php if (!empty($comet['reference_text'])): ?>
|
|
<span class="comets-badge"><?= h((string) $comet['reference_text']) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
<td><?= h((string) $comet['orbit_type_label']) ?></td>
|
|
<td><?= h((string) $comet['ra_display']) ?></td>
|
|
<td><?= h((string) $comet['dec_display']) ?></td>
|
|
<td><?= h((string) $comet['perihelion_date_display']) ?></td>
|
|
<td><span class="comets-days <?= $daysClass ?>"><?= h($daysLabel) ?></span></td>
|
|
<td title="r = <?= h((string) $comet['heliocentric_distance_display']) ?> AE, Δ = <?= h((string) $comet['geocentric_distance_display']) ?> AE"><?= h((string) $comet['estimated_magnitude_display']) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<section class="card comets-card">
|
|
<h2>Interessante Kometen</h2>
|
|
<p class="hint">Sortiert nach Naehe zum heutigen Datum bezogen auf den Periheldurchgang.</p>
|
|
<?php if (empty($interestingComets)): ?>
|
|
<p class="hint">Im aktuellen Zeitfenster wurden keine Kometen gefunden.</p>
|
|
<?php else: ?>
|
|
<div class="comets-table-wrap">
|
|
<table class="comets-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Komet</th>
|
|
<th>Typ</th>
|
|
<th>RA</th>
|
|
<th>Dec</th>
|
|
<th>Perihel</th>
|
|
<th>Abstand</th>
|
|
<th title="Grobe Helligkeitsschaetzung fuer heute nach H + 5 log(Delta) + 2,5 * n * log(r).">m grob</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($interestingComets as $comet): ?>
|
|
<?php
|
|
$daysClass = ((int) ($comet['days_from_today'] ?? 0)) < 0 ? 'is-past' : 'is-upcoming';
|
|
$daysLabel = $comet['days_from_today'] === null
|
|
? '-'
|
|
: ((int) $comet['days_from_today'] === 0
|
|
? 'Heute'
|
|
: (((int) $comet['days_from_today'] > 0 ? '+' : '') . (int) $comet['days_from_today'] . ' Tage'));
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<div class="comets-name"><?= h((string) $comet['designation_and_name']) ?></div>
|
|
<div class="comets-meta">
|
|
<?php if ((int) $comet['is_favorite'] === 1): ?>
|
|
<span class="comets-badge is-favorite">Favorit</span>
|
|
<?php elseif ((int) $comet['is_tracked'] === 1): ?>
|
|
<span class="comets-badge is-tracked">Beobachtet</span>
|
|
<?php endif; ?>
|
|
<?php if (!empty($comet['reference_text'])): ?>
|
|
<span class="comets-badge"><?= h((string) $comet['reference_text']) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
<td><?= h((string) $comet['orbit_type_label']) ?></td>
|
|
<td><?= h((string) $comet['ra_display']) ?></td>
|
|
<td><?= h((string) $comet['dec_display']) ?></td>
|
|
<td><?= h((string) $comet['perihelion_date_display']) ?></td>
|
|
<td><span class="comets-days <?= $daysClass ?>"><?= h($daysLabel) ?></span></td>
|
|
<td title="r = <?= h((string) $comet['heliocentric_distance_display']) ?> AE, Δ = <?= h((string) $comet['geocentric_distance_display']) ?> AE"><?= h((string) $comet['estimated_magnitude_display']) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
</div>
|
|
|
|
<?php require __DIR__ . '/footer.php'; ?>
|