Sternhimmlel verbessert

This commit is contained in:
2026-03-31 20:36:15 +02:00
parent 5804530345
commit 7ce96b0c85
5 changed files with 364 additions and 158 deletions
+30
View File
@@ -244,6 +244,36 @@ CREATE TABLE IF NOT EXISTS `dso_objects` (
-- Daten-Export vom Benutzer nicht ausgewählt
-- Exportiere Struktur von Tabelle d0467f49.meteoshowers
CREATE TABLE IF NOT EXISTS `meteoshowers` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`shower_code` varchar(16) NOT NULL,
`designation` varchar(255) NOT NULL,
`iau_no` int(10) unsigned DEFAULT NULL,
`speed` double DEFAULT NULL,
`radiant_alpha` double DEFAULT NULL,
`radiant_delta` double DEFAULT NULL,
`drift_alpha` double DEFAULT NULL,
`drift_delta` double DEFAULT NULL,
`parent_obj` varchar(255) DEFAULT NULL,
`pidx` double DEFAULT NULL,
`activity_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`activity_json`)),
`source_entry_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`source_entry_json`)),
`source_dataset_name` varchar(128) DEFAULT NULL,
`source_dataset_version` int(10) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_meteoshowers_shower_code` (`shower_code`),
UNIQUE KEY `uq_meteoshowers_iau_no` (`iau_no`),
KEY `idx_meteoshowers_designation` (`designation`),
KEY `idx_meteoshowers_parent_obj` (`parent_obj`),
KEY `idx_meteoshowers_speed` (`speed`),
KEY `idx_meteoshowers_pidx` (`pidx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Daten-Export vom Benutzer nicht ausgewählt
-- Exportiere Struktur von Tabelle d0467f49.geo_places
CREATE TABLE IF NOT EXISTS `geo_places` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+4 -1
View File
@@ -1,3 +1,6 @@
<?php
$publicBasePath = isset($publicBasePath) && is_string($publicBasePath) ? rtrim($publicBasePath, '/') . '/' : '';
?>
</div><!-- /.container -->
</main>
@@ -14,7 +17,7 @@
<span class="footer-location-elev"><?= number_format((float)$currentLocation['elevation'], 0) ?>&nbsp;m</span>
<?php endif; ?>
<?php if ($loggedIn && empty($locationIsGlobal)): ?>
<a href="settings.php" class="footer-location-link">&#9998;</a>
<a href="<?= htmlspecialchars($publicBasePath . 'settings.php', ENT_QUOTES, 'UTF-8') ?>" class="footer-location-link">&#9998;</a>
<?php endif; ?>
<?php endif; ?>
</p>
+14 -13
View File
@@ -12,6 +12,7 @@ $headerIntroPre = isset($headerIntroPre) && is_string($headerIntroPre) ? trim($h
$headerIntroTitle = isset($headerIntroTitle) && is_string($headerIntroTitle) ? trim($headerIntroTitle) : '';
$headerIntroSub = isset($headerIntroSub) && is_string($headerIntroSub) ? trim($headerIntroSub) : '';
$showHeaderIntro = $headerIntroPre !== '' || $headerIntroTitle !== '' || $headerIntroSub !== '';
$publicBasePath = isset($publicBasePath) && is_string($publicBasePath) ? rtrim($publicBasePath, '/') . '/' : '';
$pdoHeader = isset($pdo) ? $pdo : null;
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
$pendingApprovals = 0;
@@ -81,7 +82,7 @@ $adminLinks = [
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') ?></title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="<?= htmlspecialchars($publicBasePath . 'css/style.css', ENT_QUOTES, 'UTF-8') ?>">
</head>
<body<?= $bodyClassAttr ?>>
@@ -96,8 +97,8 @@ $adminLinks = [
// Bilder vorladen
const imgRound = new Image();
const imgRays = new Image();
imgRound.src = 'images/star16x16.png';
imgRays.src = 'images/star16x16_rays.png';
imgRound.src = <?= json_encode($publicBasePath . 'images/star16x16.png', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
imgRays.src = <?= json_encode($publicBasePath . 'images/star16x16_rays.png', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
function resize() {
canvas.width = window.innerWidth;
@@ -155,11 +156,11 @@ $adminLinks = [
<div class="container">
<div class="header-inner">
<div class="header-brand">
<a href="index.php">Skyview &amp; AstroTools</a>
<a href="<?= htmlspecialchars($publicBasePath . 'index.php', ENT_QUOTES, 'UTF-8') ?>">Skyview &amp; AstroTools</a>
<span class="header-sub"><a href="https://www.astronomiemuseum.de" target="_blank" rel="noopener">Astronomiemuseum der Sternwarte Sonneberg</a></span>
</div>
<?php if ($isAdmin && $pendingApprovals > 0): ?>
<a href="admin_users.php" class="admin-alert" aria-live="polite">
<a href="<?= htmlspecialchars($publicBasePath . 'admin_users.php', ENT_QUOTES, 'UTF-8') ?>" class="admin-alert" aria-live="polite">
<span class="admin-alert-icon" aria-hidden="true">!</span>
<span class="admin-alert-text">Achtung: <?= $pendingApprovals ?> <?= $pendingApprovals === 1 ? 'Benutzer wartet' : 'Benutzer warten' ?> auf Freischaltung</span>
</a>
@@ -176,7 +177,7 @@ $adminLinks = [
<div class="menu-group">
<div class="menu-title"><?= $group['title'] ?></div>
<?php foreach ($group['links'] as $link): ?>
<a href="<?= htmlspecialchars($link['href'], ENT_QUOTES, 'UTF-8') ?>">
<a href="<?= htmlspecialchars($publicBasePath . $link['href'], ENT_QUOTES, 'UTF-8') ?>">
<?= htmlspecialchars($link['label'], ENT_QUOTES, 'UTF-8') ?>
</a>
<?php endforeach; ?>
@@ -188,20 +189,20 @@ $adminLinks = [
<div class="menu-group">
<div class="menu-title">Konto</div>
<?php if ($loggedIn): ?>
<a href="settings.php">Einstellungen</a>
<a href="my_favorites.php">Favoriten</a>
<a href="satellitenhimmel.php">Satellitenhimmel</a>
<a href="logout.php" class="nav-logout">Abmelden</a>
<a href="<?= htmlspecialchars($publicBasePath . 'settings.php', ENT_QUOTES, 'UTF-8') ?>">Einstellungen</a>
<a href="<?= htmlspecialchars($publicBasePath . 'my_favorites.php', ENT_QUOTES, 'UTF-8') ?>">Favoriten</a>
<a href="<?= htmlspecialchars($publicBasePath . 'satellitenhimmel.php', ENT_QUOTES, 'UTF-8') ?>">Satellitenhimmel</a>
<a href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>" class="nav-logout">Abmelden</a>
<?php else: ?>
<a href="login.php">Anmelden</a>
<a href="register.php">Registrieren</a>
<a href="<?= htmlspecialchars($publicBasePath . 'login.php', ENT_QUOTES, 'UTF-8') ?>">Anmelden</a>
<a href="<?= htmlspecialchars($publicBasePath . 'register.php', ENT_QUOTES, 'UTF-8') ?>">Registrieren</a>
<?php endif; ?>
</div>
<?php if ($isAdmin): ?>
<div class="menu-group">
<div class="menu-title">Administration</div>
<?php foreach ($adminLinks as $adminLink): ?>
<a href="<?= htmlspecialchars($adminLink['href'], ENT_QUOTES, 'UTF-8') ?>">
<a href="<?= htmlspecialchars($publicBasePath . $adminLink['href'], ENT_QUOTES, 'UTF-8') ?>">
<?= htmlspecialchars($adminLink['label'], ENT_QUOTES, 'UTF-8') ?>
</a>
<?php endforeach; ?>
+10 -144
View File
@@ -923,13 +923,13 @@ if (typeof Astronomy === 'undefined') {
}
// ═══════════════════════════════════════════════════════════════════════════════
// KONFIGURATION – Defaults + optionaler fetch von config.json
// KONFIGURATION – feste Defaults
// ═══════════════════════════════════════════════════════════════════════════════
const CFG_DEFAULT = {
equatorialGrid: {
decStep: 10, raStep: 2,
colorEquator: 'rgba(70,150,255,0.60)', colorLines: 'rgba(45,95,155,0.30)',
widthEquator: 1.1, widthLines: 0.5, dash: [3,6],
widthEquator: 1.1, widthLines: 1.5, dash: [3,6],
colorLabels: 'rgba(70,120,195,0.60)', labelDecStep: 20
},
altazGrid: {
@@ -941,11 +941,11 @@ const CFG_DEFAULT = {
color: 'rgba(255,175,35,0.52)', width: 1.1, dash: [5,5]
},
constellations: {
color: 'rgba(100,160,220,0.38)', width: 0.8, dash: [4,4],
color: 'rgba(100,160,220,0.38)', width: 0.8, dash: [],
colorLabels: 'rgba(120,175,230,0.50)', fontLabels: '9px Rajdhani, sans-serif'
},
stars: {
maxRenderMag: 12.0, defaultMagLimit: 12.0,
maxRenderMag: 9.5, defaultMagLimit: 6.5,
glowBelowMag: 2.0, labelBelowMag: 2.5,
colorLabel: 'rgba(155,198,228,0.80)', fontLabel: '10px Rajdhani,sans-serif'
},
@@ -984,19 +984,7 @@ const CFG_DEFAULT = {
}
};
// Tiefes Merge: cfg überschreibt defaults rekursiv
function deepMerge(def, over) {
const out = Object.assign({}, def);
for (const k of Object.keys(over || {})) {
if (over[k] && typeof over[k] === 'object' && !Array.isArray(over[k]))
out[k] = deepMerge(def[k] || {}, over[k]);
else
out[k] = over[k];
}
return out;
}
let CFG = CFG_DEFAULT; // wird ggf. durch config.json überschrieben
let CFG = CFG_DEFAULT;
const HEADER_LOCATION = <?= json_encode($planetariumLocation, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const USER_LOCATIONS = <?= json_encode($availableLocations, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const LOCATION_BY_ID = new Map(USER_LOCATIONS.map(loc => [String(loc.id ?? ''), loc]));
@@ -1066,133 +1054,13 @@ let labelMessier = true;
let showStars = true;
// ═══════════════════════════════════════════════════════════════════════════════
// MESSIER KATALOG – 110 Objekte, J2000, Koordinaten nach SIMBAD/NED
// ra in Stunden, dec in Grad, mag: integrierte Helligkeit, size: Bogeminuten
// type: Oc=offener Haufen, Gc=Kugelhaufen, Nb=Nebel, Gx=Galaxie,
// Pn=Planetarischer Nebel, Sc=Sternhaufen+Nebel, As=Asterismus
// ═══════════════════════════════════════════════════════════════════════════════
const MESSIER_FALLBACK = [
{m:1, name:'Krebsnebel', type:'Pn', ra:5.5755, dec:22.0145, mag:8.4, size:7.0},
{m:2, name:'', type:'Gc', ra:21.5577, dec:-0.8233, mag:6.5, size:16.0},
{m:3, name:'', type:'Gc', ra:13.7033, dec:28.3772, mag:6.2, size:18.0},
{m:4, name:'', type:'Gc', ra:16.3933, dec:-26.5258, mag:5.9, size:26.0},
{m:5, name:'', type:'Gc', ra:15.3097, dec:2.0825, mag:5.8, size:17.4},
{m:6, name:'Schmetterlingshaufen', type:'Oc', ra:17.6675, dec:-32.2167, mag:4.2, size:25.0},
{m:7, name:'Ptolemäus-Haufen', type:'Oc', ra:17.8975, dec:-34.8167, mag:3.3, size:80.0},
{m:8, name:'Lagunennebel', type:'Nb', ra:18.0628, dec:-24.3833, mag:5.8, size:90.0},
{m:9, name:'', type:'Gc', ra:17.3197, dec:-18.5158, mag:7.9, size:9.3},
{m:10, name:'', type:'Gc', ra:16.9522, dec:-4.1006, mag:6.4, size:15.1},
{m:11, name:'Wildentenhaufen', type:'Oc', ra:18.8511, dec:-6.2667, mag:5.8, size:14.0},
{m:12, name:'', type:'Gc', ra:16.7872, dec:-1.9486, mag:6.7, size:14.5},
{m:13, name:'Herkuleshaufen', type:'Gc', ra:16.6947, dec:36.4603, mag:5.8, size:20.0},
{m:14, name:'', type:'Gc', ra:17.6261, dec:-3.2458, mag:7.6, size:11.7},
{m:15, name:'', type:'Gc', ra:21.4997, dec:12.1669, mag:6.2, size:12.3},
{m:16, name:'Adlernebel', type:'Sc', ra:18.3133, dec:-13.7833, mag:6.4, size:7.0},
{m:17, name:'Omeganebel', type:'Nb', ra:18.3461, dec:-16.1833, mag:6.0, size:11.0},
{m:18, name:'', type:'Oc', ra:18.3325, dec:-17.1333, mag:7.5, size:9.0},
{m:19, name:'', type:'Gc', ra:17.0433, dec:-26.2678, mag:6.8, size:13.5},
{m:20, name:'Trifidnebel', type:'Nb', ra:18.0436, dec:-23.0333, mag:6.3, size:28.0},
{m:21, name:'', type:'Oc', ra:18.0736, dec:-22.5000, mag:6.5, size:13.0},
{m:22, name:'', type:'Gc', ra:18.6053, dec:-23.9047, mag:5.1, size:24.0},
{m:23, name:'', type:'Oc', ra:17.9508, dec:-19.0167, mag:6.9, size:27.0},
{m:24, name:'Sagittarius-Sternwolke', type:'As', ra:18.2833, dec:-18.5500, mag:4.6, size:90.0},
{m:25, name:'', type:'Oc', ra:18.5258, dec:-19.2333, mag:4.6, size:32.0},
{m:26, name:'', type:'Oc', ra:18.7550, dec:-9.3833, mag:8.0, size:15.0},
{m:27, name:'Hantelnebel', type:'Pn', ra:19.9939, dec:22.7211, mag:7.5, size:8.0},
{m:28, name:'', type:'Gc', ra:18.4097, dec:-24.8697, mag:6.8, size:11.2},
{m:29, name:'', type:'Oc', ra:20.3992, dec:38.5333, mag:7.1, size:7.0},
{m:30, name:'', type:'Gc', ra:21.6728, dec:-23.1800, mag:7.2, size:11.0},
{m:31, name:'Andromedagalaxie', type:'Gx', ra:0.7122, dec:41.2692, mag:3.4, size:190.0},
{m:32, name:'', type:'Gx', ra:0.7114, dec:40.8656, mag:8.7, size:8.7},
{m:33, name:'Dreiecksgalaxie', type:'Gx', ra:1.5644, dec:30.6603, mag:5.7, size:62.0},
{m:34, name:'', type:'Oc', ra:2.7019, dec:42.7667, mag:5.5, size:35.0},
{m:35, name:'', type:'Oc', ra:6.1483, dec:24.3333, mag:5.3, size:28.0},
{m:36, name:'', type:'Oc', ra:5.6025, dec:34.1333, mag:6.3, size:12.0},
{m:37, name:'', type:'Oc', ra:5.8728, dec:32.5500, mag:6.2, size:24.0},
{m:38, name:'', type:'Oc', ra:5.4783, dec:35.8500, mag:7.4, size:21.0},
{m:39, name:'', type:'Oc', ra:21.5325, dec:48.4333, mag:4.6, size:32.0},
{m:40, name:'Winnecke 4', type:'As', ra:12.3722, dec:58.0833, mag:8.4, size:0.8},
{m:41, name:'', type:'Oc', ra:6.7669, dec:-20.7333, mag:4.5, size:38.0},
{m:42, name:'Orionnebel', type:'Nb', ra:5.5886, dec:-5.3911, mag:4.0, size:85.0},
{m:43, name:'', type:'Nb', ra:5.5944, dec:-5.2708, mag:9.0, size:20.0},
{m:44, name:'Krippenaufen/Praesepe', type:'Oc', ra:8.6678, dec:19.9833, mag:3.7, size:70.0},
{m:45, name:'Plejaden', type:'Oc', ra:3.7908, dec:24.1167, mag:1.6, size:110.0},
{m:46, name:'', type:'Oc', ra:7.6961, dec:-14.8167, mag:6.0, size:27.0},
{m:47, name:'', type:'Oc', ra:7.6103, dec:-14.5000, mag:5.2, size:30.0},
{m:48, name:'', type:'Oc', ra:8.2292, dec:-5.8000, mag:5.5, size:54.0},
{m:49, name:'', type:'Gx', ra:12.4961, dec:8.0003, mag:8.4, size:9.8},
{m:50, name:'', type:'Oc', ra:7.0494, dec:-8.3667, mag:6.3, size:16.0},
{m:51, name:'Strudelgalaxie', type:'Gx', ra:13.4997, dec:47.1953, mag:8.4, size:11.2},
{m:52, name:'', type:'Oc', ra:23.4006, dec:61.5833, mag:7.3, size:13.0},
{m:53, name:'', type:'Gc', ra:13.2158, dec:18.1681, mag:7.6, size:12.6},
{m:54, name:'', type:'Gc', ra:18.9178, dec:-30.4786, mag:7.7, size:9.1},
{m:55, name:'', type:'Gc', ra:19.6669, dec:-30.9644, mag:6.3, size:19.0},
{m:56, name:'', type:'Gc', ra:19.2758, dec:30.1847, mag:8.3, size:7.1},
{m:57, name:'Ringnebel', type:'Pn', ra:18.8931, dec:33.0289, mag:8.8, size:1.4},
{m:58, name:'', type:'Gx', ra:12.6278, dec:11.8181, mag:9.7, size:5.9},
{m:59, name:'', type:'Gx', ra:12.7003, dec:11.6472, mag:9.6, size:5.4},
{m:60, name:'', type:'Gx', ra:12.7272, dec:11.5528, mag:8.8, size:7.6},
{m:61, name:'', type:'Gx', ra:12.3650, dec:4.4736, mag:9.7, size:6.0},
{m:62, name:'', type:'Gc', ra:17.0211, dec:-30.1128, mag:6.5, size:14.1},
{m:63, name:'Sonnenblumengalaxie', type:'Gx', ra:13.2644, dec:42.0294, mag:8.6, size:12.6},
{m:64, name:'Blackeye-Galax.', type:'Gx', ra:12.9456, dec:21.6828, mag:8.5, size:9.3},
{m:65, name:'', type:'Gx', ra:11.3153, dec:13.0919, mag:9.3, size:8.7},
{m:66, name:'', type:'Gx', ra:11.3369, dec:12.9911, mag:8.9, size:9.1},
{m:67, name:'', type:'Oc', ra:8.8556, dec:11.8167, mag:6.9, size:25.0},
{m:68, name:'', type:'Gc', ra:12.6567, dec:-26.7444, mag:7.8, size:12.0},
{m:69, name:'', type:'Gc', ra:18.5231, dec:-32.3483, mag:7.6, size:7.1},
{m:70, name:'', type:'Gc', ra:18.7208, dec:-32.2917, mag:7.9, size:7.8},
{m:71, name:'', type:'Gc', ra:19.8964, dec:18.7792, mag:6.1, size:7.2},
{m:72, name:'', type:'Gc', ra:20.8908, dec:-12.5375, mag:9.3, size:5.9},
{m:73, name:'', type:'As', ra:20.9833, dec:-12.6333, mag:9.0, size:2.8},
{m:74, name:'', type:'Gx', ra:1.6114, dec:15.7836, mag:9.4, size:10.5},
{m:75, name:'', type:'Gc', ra:20.1014, dec:-21.9217, mag:8.5, size:6.0},
{m:76, name:'Kleiner Hantelnebel', type:'Pn', ra:1.7058, dec:51.5753, mag:10.1, size:2.7},
{m:77, name:'', type:'Gx', ra:2.7119, dec:-0.0133, mag:8.9, size:7.1},
{m:78, name:'', type:'Nb', ra:5.7789, dec:0.0833, mag:8.3, size:8.0},
{m:79, name:'', type:'Gc', ra:5.4017, dec:-24.5239, mag:7.7, size:9.6},
{m:80, name:'', type:'Gc', ra:16.2847, dec:-22.9758, mag:7.3, size:10.0},
{m:81, name:'Bodes Galaxie', type:'Gx', ra:9.9258, dec:69.0653, mag:6.9, size:21.0},
{m:82, name:'Zigarrengalaxie', type:'Gx', ra:9.9317, dec:69.6797, mag:8.4, size:11.2},
{m:83, name:'Südl. Feuerradgal.', type:'Gx', ra:13.6169, dec:-29.8658, mag:7.6, size:12.9},
{m:84, name:'', type:'Gx', ra:12.4197, dec:12.8869, mag:9.1, size:6.5},
{m:85, name:'', type:'Gx', ra:12.4233, dec:18.1911, mag:9.1, size:7.1},
{m:86, name:'', type:'Gx', ra:12.4361, dec:12.9461, mag:8.9, size:8.9},
{m:87, name:'Virgogalaxie', type:'Gx', ra:12.5136, dec:12.3911, mag:8.6, size:7.2},
{m:88, name:'', type:'Gx', ra:12.5319, dec:14.4197, mag:9.6, size:6.9},
{m:89, name:'', type:'Gx', ra:12.5942, dec:12.5564, mag:9.8, size:5.1},
{m:90, name:'', type:'Gx', ra:12.6133, dec:13.1628, mag:9.5, size:9.5},
{m:91, name:'', type:'Gx', ra:12.5917, dec:14.4961, mag:10.2, size:5.4},
{m:92, name:'', type:'Gc', ra:17.2850, dec:43.1358, mag:6.4, size:11.2},
{m:93, name:'', type:'Oc', ra:7.7417, dec:-23.8500, mag:6.0, size:22.0},
{m:94, name:'', type:'Gx', ra:12.8481, dec:41.1203, mag:8.2, size:7.4},
{m:95, name:'', type:'Gx', ra:10.7328, dec:11.7039, mag:9.7, size:7.4},
{m:96, name:'', type:'Gx', ra:10.7794, dec:11.8200, mag:9.2, size:7.6},
{m:97, name:'Eulennebel', type:'Pn', ra:11.2481, dec:55.0194, mag:9.9, size:3.4},
{m:98, name:'', type:'Gx', ra:12.2322, dec:14.9003, mag:10.1, size:9.8},
{m:99, name:'', type:'Gx', ra:12.3133, dec:14.4167, mag:9.9, size:5.4},
{m:100, name:'', type:'Gx', ra:12.3819, dec:15.8228, mag:9.3, size:7.4},
{m:101, name:'Feuerradgalaxie', type:'Gx', ra:14.0531, dec:54.3489, mag:7.9, size:22.0},
{m:102, name:'Spindelgalaxie', type:'Gx', ra:15.1086, dec:55.7631, mag:9.9, size:5.2},
{m:103, name:'', type:'Oc', ra:1.5553, dec:60.6500, mag:7.4, size:6.0},
{m:104, name:'Sombrerogalaxie', type:'Gx', ra:12.6664, dec:-11.6231, mag:8.0, size:8.7},
{m:105, name:'', type:'Gx', ra:10.7978, dec:12.5814, mag:9.8, size:4.6},
{m:106, name:'', type:'Gx', ra:12.3161, dec:47.3039, mag:8.4, size:18.6},
{m:107, name:'', type:'Gc', ra:16.5419, dec:-13.0531, mag:7.9, size:10.0},
{m:108, name:'', type:'Gx', ra:11.1917, dec:55.6744, mag:10.0, size:8.7},
{m:109, name:'', type:'Gx', ra:11.9578, dec:53.3747, mag:9.8, size:7.6},
{m:110, name:'', type:'Gx', ra:0.6733, dec:41.6853, mag:8.5, size:17.0},
];
const EMBEDDED_CONSTELLATION_DATA = <?php echo $constellationDataJson ?: '{"constellations":[]}'; ?>;
const EMBEDDED_CONSTELLATION_ERROR = <?php echo $constellationDataErrorJson ?: 'null'; ?>;
const EMBEDDED_STAR_DATA = <?php echo $starDataJson ?: '[]'; ?>;
const EMBEDDED_STAR_ERROR = <?php echo $starDataErrorJson ?: 'null'; ?>;
const EMBEDDED_MESSIER_DATA = <?php echo $messierDataJson ?: '[]'; ?>;
const EMBEDDED_MESSIER_ERROR = <?php echo $messierDataErrorJson ?: 'null'; ?>;
const MESSIER = Array.isArray(EMBEDDED_MESSIER_DATA) && EMBEDDED_MESSIER_DATA.length
? EMBEDDED_MESSIER_DATA
: MESSIER_FALLBACK;
const MESSIER = Array.isArray(EMBEDDED_MESSIER_DATA) ? EMBEDDED_MESSIER_DATA : [];
let constellationData = EMBEDDED_CONSTELLATION_DATA;
let hipMap = new Map();
@@ -2336,13 +2204,9 @@ document.getElementById('magLimitMessier').addEventListener('input', function()
// KATALOG LADEN – automatisch per fetch, Fallback auf Datei-Dialog
// ═══════════════════════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════════════════════
// RESSOURCEN LADEN – config · Sterne aus DB
// RESSOURCEN LADEN – Sterne aus DB
// Auto-Export erst wenn alles fertig ist
// ═══════════════════════════════════════════════════════════════════════════════
const pConfig = fetch('config.json')
.then(r => { if (!r.ok) throw new Error(); return r.json(); })
.then(data => { CFG = deepMerge(CFG_DEFAULT, data); M_STYLE = buildMStyle(); })
.catch(() => { /* Fallback auf CFG_DEFAULT – bereits gesetzt */ });
if (EMBEDDED_CONSTELLATION_ERROR) {
console.warn('Sternbilddaten aus der Datenbank nicht geladen:', EMBEDDED_CONSTELLATION_ERROR);
@@ -2387,7 +2251,7 @@ const pStars = Promise.resolve().then(() => {
console.info('Sterne aus DB geladen:', stars.length);
});
Promise.all([pConfig, pStars]).then(() => {
pStars.then(() => {
applyAutoMagLimitFromZoom();
syncMagLimitUi();
applyAutoMessierMagLimitFromZoom();
@@ -2549,3 +2413,5 @@ resize();
}); // end window.onload
</script>
<?php require __DIR__ . '/footer.php'; ?>
+306
View File
@@ -0,0 +1,306 @@
<php
declare(strict_types=1);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
$remoteAddr = (string)($_SERVER['REMOTE_ADDR'] ?? '');
$serverName = strtolower((string)($_SERVER['SERVER_NAME'] ?? ''));
$isLocalRequest = in_array($remoteAddr, ['127.0.0.1', '::1'], true)
|| in_array($serverName, ['localhost', '127.0.0.1', '::1'], true);
$canUseImportPage = $isAdmin || $isLocalRequest;
$pageTitle = 'Meteor-Import';
function h(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
function toNullableFloat(mixed $value): ?float
{
if ($value === null || $value === '') {
return null;
}
if (is_string($value)) {
$value = str_replace(',', '.', trim($value));
}
return is_numeric($value) ? (float)$value : null;
}
function toNullableInt(mixed $value): ?int
{
if ($value === null || $value === '') {
return null;
}
if (is_string($value)) {
$value = trim($value);
}
return preg_match('/^\d+$/', (string)$value) ? (int)$value : null;
}
function ensureMeteorShowersTable(PDO $pdo): void
{
$pdo->exec(
"CREATE TABLE IF NOT EXISTS `meteoshowers` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`shower_code` varchar(16) NOT NULL,
`designation` varchar(255) NOT NULL,
`iau_no` int(10) unsigned DEFAULT NULL,
`speed` double DEFAULT NULL,
`radiant_alpha` double DEFAULT NULL,
`radiant_delta` double DEFAULT NULL,
`drift_alpha` double DEFAULT NULL,
`drift_delta` double DEFAULT NULL,
`parent_obj` varchar(255) DEFAULT NULL,
`pidx` double DEFAULT NULL,
`activity_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`activity_json`)),
`source_entry_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`source_entry_json`)),
`source_dataset_name` varchar(128) DEFAULT NULL,
`source_dataset_version` int(10) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_meteoshowers_shower_code` (`shower_code`),
UNIQUE KEY `uq_meteoshowers_iau_no` (`iau_no`),
KEY `idx_meteoshowers_designation` (`designation`),
KEY `idx_meteoshowers_parent_obj` (`parent_obj`),
KEY `idx_meteoshowers_speed` (`speed`),
KEY `idx_meteoshowers_pidx` (`pidx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
}
$defaultJsonPath = realpath(__DIR__ . '/MeteorShowers.json') ?: (__DIR__ . '/MeteorShowers.json');
$selectedJsonPath = (string)$defaultJsonPath;
$report = null;
$errorMessage = null;
if ($canUseImportPage && $_SERVER['REQUEST_METHOD'] === 'POST') {
$jsonPath = trim((string)($_POST['json_path'] ?? ''));
if ($jsonPath === '') {
$jsonPath = (string)$defaultJsonPath;
}
$selectedJsonPath = $jsonPath;
try {
if (!is_file($jsonPath)) {
throw new RuntimeException('JSON-Datei nicht gefunden: ' . $jsonPath);
}
$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,
]
);
ensureMeteorShowersTable($pdo);
$json = file_get_contents($jsonPath);
if ($json === false) {
throw new RuntimeException('JSON-Datei konnte nicht gelesen werden.');
}
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$showers = $data['showers'] ?? null;
if (!is_array($showers)) {
throw new RuntimeException('Ungueltige JSON-Struktur: "showers" fehlt oder ist kein Objekt.');
}
$datasetName = isset($data['shortName']) ? trim((string)$data['shortName']) : null;
$datasetVersion = toNullableInt($data['version'] ?? null);
$selectStmt = $pdo->prepare('SELECT `id` FROM `meteoshowers` WHERE `shower_code` = :shower_code LIMIT 1');
$upsertStmt = $pdo->prepare(
"INSERT INTO `meteoshowers` (
`shower_code`,
`designation`,
`iau_no`,
`speed`,
`radiant_alpha`,
`radiant_delta`,
`drift_alpha`,
`drift_delta`,
`parent_obj`,
`pidx`,
`activity_json`,
`source_entry_json`,
`source_dataset_name`,
`source_dataset_version`
) VALUES (
:shower_code,
:designation,
:iau_no,
:speed,
:radiant_alpha,
:radiant_delta,
:drift_alpha,
:drift_delta,
:parent_obj,
:pidx,
:activity_json,
:source_entry_json,
:source_dataset_name,
:source_dataset_version
)
ON DUPLICATE KEY UPDATE
`designation` = VALUES(`designation`),
`iau_no` = VALUES(`iau_no`),
`speed` = VALUES(`speed`),
`radiant_alpha` = VALUES(`radiant_alpha`),
`radiant_delta` = VALUES(`radiant_delta`),
`drift_alpha` = VALUES(`drift_alpha`),
`drift_delta` = VALUES(`drift_delta`),
`parent_obj` = VALUES(`parent_obj`),
`pidx` = VALUES(`pidx`),
`activity_json` = VALUES(`activity_json`),
`source_entry_json` = VALUES(`source_entry_json`),
`source_dataset_name` = VALUES(`source_dataset_name`),
`source_dataset_version` = VALUES(`source_dataset_version`),
`updated_at` = CURRENT_TIMESTAMP()"
);
$processed = 0;
$inserted = 0;
$updated = 0;
$pdo->beginTransaction();
foreach ($showers as $code => $entry) {
if (!is_array($entry)) {
continue;
}
$showerCode = strtoupper(trim((string)$code));
if ($showerCode === '') {
continue;
}
$selectStmt->execute([':shower_code' => $showerCode]);
$exists = $selectStmt->fetchColumn() !== false;
$designation = trim((string)($entry['designation'] ?? $showerCode));
if ($designation === '') {
$designation = $showerCode;
}
$activity = isset($entry['activity']) && is_array($entry['activity']) ? $entry['activity'] : [];
$upsertStmt->execute([
':shower_code' => $showerCode,
':designation' => $designation,
':iau_no' => toNullableInt($entry['IAUNo'] ?? null),
':speed' => toNullableFloat($entry['speed'] ?? null),
':radiant_alpha' => toNullableFloat($entry['radiantAlpha'] ?? null),
':radiant_delta' => toNullableFloat($entry['radiantDelta'] ?? null),
':drift_alpha' => toNullableFloat($entry['driftAlpha'] ?? null),
':drift_delta' => toNullableFloat($entry['driftDelta'] ?? null),
':parent_obj' => isset($entry['parentObj']) && trim((string)$entry['parentObj']) !== '' ? trim((string)$entry['parentObj']) : null,
':pidx' => toNullableFloat($entry['pidx'] ?? null),
':activity_json' => json_encode($activity, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
':source_entry_json' => json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
':source_dataset_name' => $datasetName !== null && $datasetName !== '' ? $datasetName : null,
':source_dataset_version' => $datasetVersion,
]);
$processed++;
if ($exists) {
$updated++;
} else {
$inserted++;
}
}
$pdo->commit();
$tableCount = (int)$pdo->query('SELECT COUNT(*) FROM `meteoshowers`')->fetchColumn();
$report = [
'json_path' => $jsonPath,
'dataset_name' => $datasetName,
'dataset_version' => $datasetVersion,
'processed' => $processed,
'inserted' => $inserted,
'updated' => $updated,
'table_count' => $tableCount,
];
} catch (Throwable $e) {
if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
$pdo->rollBack();
}
$errorMessage = $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= h($pageTitle) ?></title>
</head>
<body style="margin:0;padding:24px;font-family:Arial,sans-serif;background:#f5f5f5;color:#111;">
<div style="max-width:900px;margin:0 auto;background:#fff;border:1px solid #ccc;padding:24px;">
<h1 style="margin:0 0 12px;">Meteor-Import</h1>
<p style="margin:0 0 18px;">
Importiert <code>MeteorShowers.json</code> in die Tabelle <code>meteoshowers</code>.
Die Tabelle wird bei Bedarf automatisch angelegt.
</p>
<?php if (!$canUseImportPage): ?>
<div style="border:1px solid #c33;background:#fff3f3;color:#900;padding:14px;">
Diese Seite ist nur fuer Admins oder lokale Aufrufe auf localhost freigegeben.
</div>
<?php else: ?>
<form method="post" style="display:grid;gap:12px;">
<label style="display:grid;gap:6px;">
<span>JSON-Pfad</span>
<input
type="text"
name="json_path"
value="<?= h($selectedJsonPath) ?>"
style="width:100%;padding:10px 12px;border:1px solid #999;color:#111;"
>
</label>
<div>
<button type="submit" style="padding:10px 16px;border:1px solid #333;background:#eee;color:#111;cursor:pointer;">
Import Starten
</button>
</div>
</form>
<?php if ($errorMessage !== null): ?>
<div style="margin-top:18px;border:1px solid #c33;background:#fff3f3;color:#900;padding:14px;">
<strong>Fehler:</strong> <?= h($errorMessage) ?>
</div>
<?php endif; ?>
<?php if (is_array($report)): ?>
<div style="margin-top:18px;border:1px solid #3a7a3a;background:#f1fff1;color:#145214;padding:14px;line-height:1.7;">
<strong>Import abgeschlossen.</strong><br>
Datei: <?= h((string)$report['json_path']) ?><br>
Datensatz: <?= h((string)($report['dataset_name'] ?? '-')) ?><br>
Version: <?= h((string)($report['dataset_version'] ?? '-')) ?><br>
Verarbeitet: <?= h((string)$report['processed']) ?><br>
Neu: <?= h((string)$report['inserted']) ?><br>
Aktualisiert: <?= h((string)$report['updated']) ?><br>
Zeilen in `meteoshowers`: <?= h((string)$report['table_count']) ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</body>
</html>