1061 lines
57 KiB
PHP
1061 lines
57 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
ini_set('display_errors', '1');
|
|
error_reporting(E_ALL);
|
|
session_start();
|
|
|
|
$loggedIn = isset($_SESSION['user_id']);
|
|
$pageTitle = 'Tagbogen - AstroTools';
|
|
$bodyClass = 'tagbogen-page';
|
|
?>
|
|
<?php require __DIR__ . '/header.php'; ?>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,300;1,400&family=Space+Mono:wght@400;700&family=Orbitron:wght@400;600;700;900&display=swap" rel="stylesheet">
|
|
<script src="js/astronomy.browser.min.js"></script>
|
|
|
|
<div class="tagbogen-tool">
|
|
<div class="modal-overlay" id="modalOverlay">
|
|
<div class="modal">
|
|
<div class="modal-head">
|
|
<div>
|
|
<div class="modal-head-title">STANDORT AUSW?HLEN</div>
|
|
<div class="modal-head-sub">Beobachtungsort auf der Erde</div>
|
|
</div>
|
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- Suche + Geolocation -->
|
|
<div style="display:flex;gap:8px;align-items:flex-end;">
|
|
<div style="flex:1">
|
|
<div class="modal-sect-label">Suche</div>
|
|
<input class="coord-input" id="locSearch" type="text" placeholder="Name eingeben..." style="width:100%" oninput="filterLocs(this.value)">
|
|
</div>
|
|
<button class="geo-btn" id="geoBtn" onclick="geoLocate()" title="Standort über Browser ermitteln">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="15" height="15">
|
|
<circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3"/>
|
|
<path d="M12 8a4 4 0 100 8 4 4 0 000-8z" opacity=".3"/>
|
|
</svg>
|
|
<span id="geoBtnText">GPS</span>
|
|
</button>
|
|
</div>
|
|
<!-- Gruppen-Dropdown -->
|
|
<div id="locGroups"></div>
|
|
<hr style="border:none;border-top:1px solid rgba(200,168,75,.07);">
|
|
<div>
|
|
<div class="modal-sect-label">Eigene Koordinaten</div>
|
|
<div class="coord-row">
|
|
<div class="coord-field">
|
|
<div class="coord-label">Breitengrad °N</div>
|
|
<input class="coord-input" id="inputLat" type="number" step="0.01" min="-90" max="90" placeholder="z.B. 48.14">
|
|
</div>
|
|
<div class="coord-field">
|
|
<div class="coord-label">L?ngengrad °E</div>
|
|
<input class="coord-input" id="inputLon" type="number" step="0.01" min="-180" max="180" placeholder="z.B. 11.58">
|
|
</div>
|
|
</div>
|
|
<div class="coord-field" style="margin-top:8px;">
|
|
<div class="coord-label">Ortsname (optional)</div>
|
|
<input class="coord-input" id="inputName" type="text" placeholder="Eigener Ort">
|
|
</div>
|
|
<button class="apply-btn" onclick="applyCustomCoords()">KOORDINATEN ANWENDEN</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="page">
|
|
<div class="header">
|
|
<div class="header-left">
|
|
<div class="header-pre">Astronomische Visualisierung - Astronomy Engine</div>
|
|
<div class="header-title">TAGBOGEN</div>
|
|
<div class="header-sub">Sonnenweg am Himmelsgew?lbe</div>
|
|
</div>
|
|
<div class="header-right">
|
|
<button class="loc-btn" onclick="openModal()">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 1118 0z"/><circle cx="12" cy="10" r="3"/>
|
|
</svg>
|
|
<span id="locBtnText">STANDORT</span>
|
|
</button>
|
|
<div class="date-ctrl">
|
|
<button class="day-btn" onclick="shiftDay(-1)" title="Einen Tag zur?ck"><</button>
|
|
<input type="date" class="date-input-hdr" id="dateInput">
|
|
<button class="day-btn" onclick="shiftDay(+1)" title="Einen Tag vor">></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="app-layout">
|
|
<div class="controls-panel">
|
|
<div class="ctrl-block">
|
|
<div class="ctrl-label">Standort</div>
|
|
<div class="info-row"><span class="info-lbl">Ort</span><span class="info-val" id="infoName">-</span></div>
|
|
<div class="info-row"><span class="info-lbl">Breite</span><span class="info-val" id="infoLat">-</span></div>
|
|
<div class="info-row"><span class="info-lbl">L?nge</span><span class="info-val" id="infoLon">-</span></div>
|
|
</div>
|
|
<div class="ctrl-block">
|
|
<div class="ctrl-label">Sonnenzeiten (UTC)</div>
|
|
<div class="stat-grid">
|
|
<div class="stat-item"><div class="stat-lbl">Aufgang</div><div class="stat-val sm" id="statRise">-</div></div>
|
|
<div class="stat-item"><div class="stat-lbl">Untergang</div><div class="stat-val sm" id="statSet">-</div></div>
|
|
<div class="stat-item"><div class="stat-lbl">Mittag</div><div class="stat-val sm" id="statNoon">-</div></div>
|
|
<div class="stat-item"><div class="stat-lbl">Taglaenge</div><div class="stat-val sm" id="statDay">-</div></div>
|
|
</div>
|
|
</div>
|
|
<div class="ctrl-block">
|
|
<div class="ctrl-label">Aktuelle Position</div>
|
|
<div class="stat-grid">
|
|
<div class="stat-item"><div class="stat-lbl">H?he</div><div class="stat-val" id="statElev">-°</div></div>
|
|
<div class="stat-item"><div class="stat-lbl">Azimut</div><div class="stat-val" id="statAz">-°</div></div>
|
|
</div>
|
|
<div style="margin-top:9px;">
|
|
<div class="info-row"><span class="info-lbl">Himmelsrichtung</span><span class="info-val" id="statDir">-</span></div>
|
|
<div class="info-row" style="margin-bottom:0"><span class="info-lbl">Tagesphase</span><span class="info-val" id="statPhase">-</span></div>
|
|
<div class="sun-phase-bar"><div class="sun-phase-fill" id="phaseBar" style="width:0%"></div></div>
|
|
</div>
|
|
</div>
|
|
<div class="ctrl-block">
|
|
<div class="ctrl-label">Sonnenparameter</div>
|
|
<div class="info-row"><span class="info-lbl">Deklination</span><span class="info-val" id="statDecl">-°</span></div>
|
|
<div class="info-row"><span class="info-lbl">Max. Elevation</span><span class="info-val" id="statMaxElev">-°</span></div>
|
|
<div class="info-row" style="margin-bottom:0"><span class="info-lbl">Aufgang-Azimut</span><span class="info-val" id="statRiseAz">-°</span></div>
|
|
</div>
|
|
<div class="ctrl-block">
|
|
<div class="ctrl-label">Jahresvergleich</div>
|
|
<div style="font-size:10px;color:rgba(200,168,75,.55);margin-bottom:9px;line-height:1.6;">?berlagerung der Sonnenb?gen f?r astronomisch wichtige Tage</div>
|
|
<div style="display:flex;flex-direction:column;gap:6px;">
|
|
<button class="overlay-btn" id="btn_sommer" onclick="toggleOverlay('sommer')" data-active="0">
|
|
<span class="overlay-dot" style="background:#ff6020;color:#ff6020"></span>
|
|
<span class="overlay-text">Sommersonnenwende</span>
|
|
<div class="overlay-tooltip" id="tip_sommer">
|
|
<div class="overlay-tooltip-title">SOMMERSONNENWENDE</div>
|
|
<div class="overlay-tooltip-row"><span>Datum</span><span id="tip_sommer_date">21. Jun.</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang</span><span id="tip_sommer_rise">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Untergang</span><span id="tip_sommer_set">-</span></div>
|
|
<div class="overlay-tooltip-row highlight"><span>Taglaenge</span><span id="tip_sommer_dur">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Max. Elevation</span><span id="tip_sommer_elev">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang-Azimut</span><span id="tip_sommer_az">-</span></div>
|
|
</div>
|
|
</button>
|
|
<button class="overlay-btn" id="btn_winter" onclick="toggleOverlay('winter')" data-active="0">
|
|
<span class="overlay-dot" style="background:#4080ff;color:#4080ff"></span>
|
|
<span class="overlay-text">Wintersonnenwende</span>
|
|
<div class="overlay-tooltip" id="tip_winter">
|
|
<div class="overlay-tooltip-title">WINTERSONNENWENDE</div>
|
|
<div class="overlay-tooltip-row"><span>Datum</span><span id="tip_winter_date">21. Dez.</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang</span><span id="tip_winter_rise">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Untergang</span><span id="tip_winter_set">-</span></div>
|
|
<div class="overlay-tooltip-row highlight"><span>Taglaenge</span><span id="tip_winter_dur">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Max. Elevation</span><span id="tip_winter_elev">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang-Azimut</span><span id="tip_winter_az">-</span></div>
|
|
</div>
|
|
</button>
|
|
<button class="overlay-btn" id="btn_fruehjahr" onclick="toggleOverlay('fruehjahr')" data-active="0">
|
|
<span class="overlay-dot" style="background:#40d080;color:#40d080"></span>
|
|
<span class="overlay-text">Tag-/Nachtgleiche</span>
|
|
<div class="overlay-tooltip" id="tip_fruehjahr">
|
|
<div class="overlay-tooltip-title">TAG-/NACHTGLEICHE</div>
|
|
<div class="overlay-tooltip-row"><span>Datum</span><span id="tip_fruehjahr_date">20. M?r.</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang</span><span id="tip_fruehjahr_rise">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Untergang</span><span id="tip_fruehjahr_set">-</span></div>
|
|
<div class="overlay-tooltip-row highlight"><span>Taglaenge</span><span id="tip_fruehjahr_dur">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Max. Elevation</span><span id="tip_fruehjahr_elev">-</span></div>
|
|
<div class="overlay-tooltip-row"><span>Aufgang-Azimut</span><span id="tip_fruehjahr_az">-</span></div>
|
|
</div>
|
|
</button>
|
|
|
|
</div>
|
|
<button class="overlay-clear-btn" onclick="clearOverlays()" style="margin-top:9px;">Alle ausblenden</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="viz-panel">
|
|
<div class="viz-wrap">
|
|
<canvas id="arcCanvas"></canvas>
|
|
<div class="arc-overlay"><div class="arc-badge" id="arcBadge">TAGBOGEN</div></div>
|
|
<div class="view-toggle"><button class="vtb active" id="vtb_allsky" onclick="setView('allsky')">Zenit</button><button class="vtb" id="vtb_horizon" onclick="setView('horizon')">Horizont</button></div>
|
|
<div class="time-display">
|
|
<div class="time-big" id="timeBig">--:--</div>
|
|
<div class="time-sub">UTC</div>
|
|
</div>
|
|
</div>
|
|
<div class="slider-wrap">
|
|
<div class="slider-header">
|
|
<span class="slider-label" id="sliderLabel">TAGESVERLAUF</span>
|
|
<span class="slider-time" id="sliderTimeLabel">--:--</span>
|
|
</div>
|
|
<div class="sun-slider-track">
|
|
<div class="sun-slider-bg"><div class="sun-gradient-fill"></div></div>
|
|
<div class="sun-track-labels">
|
|
<span class="sun-track-lbl" id="lbl_rise">- O (Aufgang)</span>
|
|
<span class="sun-track-lbl" id="lbl_noon">-</span>
|
|
<span class="sun-track-lbl" id="lbl_set">(Untergang) W -</span>
|
|
</div>
|
|
<input type="range" id="timeSlider" min="0" max="1000" value="500" oninput="onSlider()">
|
|
</div>
|
|
<div class="time-ticks" id="timeTicks"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
//
|
|
//
|
|
//
|
|
|
|
//
|
|
//
|
|
const LOC_GROUPS = [
|
|
{ group: 'Deutschland & Nachbarl?nder', items: [
|
|
{name:'Rödental', sub:'Bayern, DE', lat: 50.30, lon: 11.05},
|
|
{name:'Berlin', sub:'DE, 52°N', lat: 52.52, lon: 13.40},
|
|
{name:'München', sub:'Bayern, DE', lat: 48.14, lon: 11.58},
|
|
{name:'Hamburg', sub:'DE, 53°N', lat: 53.55, lon: 10.00},
|
|
{name:'Köln', sub:'NRW, DE', lat: 50.94, lon: 6.96},
|
|
{name:'Frankfurt', sub:'Hessen, DE', lat: 50.11, lon: 8.68},
|
|
{name:'Dresden', sub:'Sachsen, DE', lat: 51.05, lon: 13.74},
|
|
{name:'Flensburg', sub:'Norddeutschland', lat: 54.79, lon: 9.44},
|
|
{name:'Zugspitze', sub:'2962 m, Bayern', lat: 47.42, lon: 10.99},
|
|
{name:'Wien', sub:'Österreich', lat: 48.21, lon: 16.37},
|
|
{name:'Innsbruck', sub:'Tirol, AT', lat: 47.27, lon: 11.39},
|
|
{name:'Zürich', sub:'Schweiz', lat: 47.38, lon: 8.54},
|
|
{name:'Bern', sub:'Schweiz', lat: 46.95, lon: 7.44},
|
|
{name:'Genf', sub:'Schweiz, 46°N', lat: 46.20, lon: 6.15},
|
|
{name:'Prag', sub:'Tschechien', lat: 50.08, lon: 14.42},
|
|
{name:'Warschau', sub:'Polen', lat: 52.23, lon: 21.01},
|
|
{name:'Amsterdam', sub:'Niederlande', lat: 52.37, lon: 4.90},
|
|
{name:'Brüssel', sub:'Belgien', lat: 50.85, lon: 4.35},
|
|
]},
|
|
{ group: 'Sternwarten & Observatorien', items: [
|
|
{name:'Sternwarte Sonneberg', sub:'Thüringen, 50.4°N', lat: 50.36, lon: 11.19},
|
|
{name:'Archenhold Berlin', sub:'Treptow, 52.5°N', lat: 52.48, lon: 13.47},
|
|
{name:'Deutsches Museum München',sub:'Isartor', lat: 48.13, lon: 11.58},
|
|
{name:'Sternwarte Heidelberg', sub:'Königstuhl', lat: 49.40, lon: 8.72},
|
|
{name:'H?her List Eifel', sub:'Rheinland, 50°N', lat: 50.16, lon: 6.85},
|
|
{name:'Calar Alto', sub:'Spanien, 2168 m', lat: 37.22, lon: -2.55},
|
|
{name:'ESO La Silla', sub:'Chile, 2400 m', lat:-29.26, lon:-70.73},
|
|
{name:'ESO Paranal (VLT)', sub:'Chile, 2635 m', lat:-24.63, lon:-70.40},
|
|
{name:'Mauna Kea', sub:'Hawaii, 4205 m', lat: 19.82, lon:-155.47},
|
|
{name:'La Palma (Roque)', sub:'Kanaren, 2396 m', lat: 28.76, lon:-17.89},
|
|
{name:'Kitt Peak', sub:'Arizona, USA', lat: 31.96, lon:-111.60},
|
|
{name:'Greenwich', sub:'London, 0° Merid.',lat: 51.48, lon: 0.00},
|
|
{name:'Arecibo', sub:'Puerto Rico', lat: 18.34, lon:-66.75},
|
|
{name:'ALMA', sub:'Chile, 5058 m', lat:-23.03, lon:-67.75},
|
|
{name:'SKA Karoo', sub:'Südafrika', lat:-30.71, lon: 21.44},
|
|
{name:'FAST China', sub:'Guizhou, 25°N', lat: 25.65, lon:106.86},
|
|
{name:'Effelsberg', sub:'Eifel, 50.5°N', lat: 50.52, lon: 6.88},
|
|
{name:'Mt. Palomar', sub:'Kalifornien', lat: 33.36, lon:-116.86},
|
|
{name:'Cerro Pachón (LSST)', sub:'Chile, 2662 m', lat:-30.24, lon:-70.74},
|
|
{name:'Sternwarte Bochum', sub:'NRW, 51.5°N', lat: 51.48, lon: 7.22},
|
|
]},
|
|
{ group: 'Europa', items: [
|
|
{name:'Paris', sub:'Frankreich', lat: 48.85, lon: 2.35},
|
|
{name:'London', sub:'UK, 51.5°N', lat: 51.51, lon: -0.13},
|
|
{name:'Madrid', sub:'Spanien', lat: 40.42, lon: -3.70},
|
|
{name:'Barcelona', sub:'Katalonien', lat: 41.39, lon: 2.15},
|
|
{name:'Rom', sub:'Italien, 42°N', lat: 41.90, lon: 12.50},
|
|
{name:'Mailand', sub:'Norditalien', lat: 45.46, lon: 9.19},
|
|
{name:'Athen', sub:'Griechenland', lat: 37.98, lon: 23.73},
|
|
{name:'Istanbul', sub:'Türkei, 41°N', lat: 41.02, lon: 28.97},
|
|
{name:'Lissabon', sub:'Portugal, 38.7°N', lat: 38.72, lon: -9.14},
|
|
{name:'Oslo', sub:'Norwegen, 60°N', lat: 59.91, lon: 10.75},
|
|
{name:'Stockholm', sub:'Schweden, 59°N', lat: 59.33, lon: 18.07},
|
|
{name:'Helsinki', sub:'Finnland, 60°N', lat: 60.17, lon: 24.94},
|
|
{name:'Kopenhagen', sub:'Dänemark, 56°N', lat: 55.68, lon: 12.57},
|
|
{name:'Reykjavik', sub:'Island, 64°N', lat: 64.13, lon:-21.94},
|
|
{name:'Tromsø', sub:'Norwegen, 70°N', lat: 69.65, lon: 18.96},
|
|
{name:'Longyearbyen', sub:'Svalbard, 78°N', lat: 78.22, lon: 15.63},
|
|
{name:'Lofoten', sub:'Norwegen, 68°N', lat: 68.11, lon: 13.58},
|
|
{name:'Moskau', sub:'Russland, 56°N', lat: 55.75, lon: 37.62},
|
|
{name:'Murmansk', sub:'Russland, 69°N', lat: 68.97, lon: 33.07},
|
|
]},
|
|
{ group: 'Amerika', items: [
|
|
{name:'New York', sub:'USA, 40.7°N', lat: 40.71, lon:-74.01},
|
|
{name:'Los Angeles', sub:'Kalifornien', lat: 34.05, lon:-118.24},
|
|
{name:'Chicago', sub:'Illinois, 42°N', lat: 41.85, lon:-87.65},
|
|
{name:'Miami', sub:'Florida, 26°N', lat: 25.77, lon:-80.19},
|
|
{name:'Anchorage', sub:'Alaska, 61°N', lat: 61.22, lon:-149.90},
|
|
{name:'Barrow (Utqiaġvik)',sub:'Alaska, 71°N', lat: 71.30, lon:-156.79},
|
|
{name:'Mexiko-Stadt', sub:'Mexiko, 19°N', lat: 19.43, lon:-99.13},
|
|
{name:'Bogotá', sub:'Kolumbien, 4.7°N', lat: 4.71, lon:-74.07},
|
|
{name:'Quito', sub:'Ecuador, 0.2°S', lat: -0.23, lon:-78.52},
|
|
{name:'Lima', sub:'Peru, 12°S', lat:-12.05, lon:-77.04},
|
|
{name:'Santiago', sub:'Chile, 33.5°S', lat:-33.46, lon:-70.65},
|
|
{name:'Buenos Aires', sub:'Argentinien, 34°S', lat:-34.61, lon:-58.38},
|
|
{name:'Punta Arenas', sub:'Chile, 53°S', lat:-53.16, lon:-70.91},
|
|
{name:'Ushuaia', sub:'Argentinien, 55°S', lat:-54.80, lon:-68.30},
|
|
]},
|
|
{ group: 'Asien & Pazifik', items: [
|
|
{name:'Tokio', sub:'Japan, 35.7°N', lat: 35.68, lon:139.69},
|
|
{name:'Sapporo', sub:'Hokkaido, 43°N', lat: 43.06, lon:141.35},
|
|
{name:'Peking', sub:'China, 40°N', lat: 39.91, lon:116.39},
|
|
{name:'Shanghai', sub:'China, 31°N', lat: 31.23, lon:121.47},
|
|
{name:'Hongkong', sub:'22.3°N', lat: 22.32, lon:114.17},
|
|
{name:'Singapur', sub:'1.4°N, fast Ãquator', lat: 1.35, lon:103.82},
|
|
{name:'Mumbai', sub:'Indien, 19°N', lat: 19.08, lon: 72.88},
|
|
{name:'Delhi', sub:'Indien, 28.6°N', lat: 28.61, lon: 77.21},
|
|
{name:'Riad', sub:'Saudi-Arabien, 25°N', lat: 24.68, lon: 46.72},
|
|
{name:'Dubai', sub:'VAE, 25.2°N', lat: 25.20, lon: 55.27},
|
|
{name:'Sydney', sub:'Australien, 34°S', lat:-33.87, lon:151.21},
|
|
{name:'Melbourne', sub:'Australien, 37.8°S', lat:-37.81, lon:144.96},
|
|
{name:'Auckland', sub:'Neuseeland, 37°S', lat:-36.86, lon:174.77},
|
|
{name:'Hobart', sub:'Tasmanien, 43°S', lat:-42.88, lon:147.32},
|
|
]},
|
|
{ group: 'Afrika & Naher Osten', items: [
|
|
{name:'Kairo', sub:'Ãgypten, 30°N', lat: 30.05, lon: 31.24},
|
|
{name:'Kapstadt', sub:'Südafrika, 34°S', lat:-33.93, lon: 18.42},
|
|
{name:'Johannesburg', sub:'Südafrika, 26.2°S', lat:-26.20, lon: 28.04},
|
|
{name:'Nairobi', sub:'Kenia, 1.3°S', lat: -1.29, lon: 36.82},
|
|
{name:'Addis Abeba', sub:'Ãthiopien, 9°N', lat: 9.03, lon: 38.74},
|
|
{name:'Casablanca', sub:'Marokko, 33.6°N', lat: 33.59, lon: -7.62},
|
|
]},
|
|
{ group: 'Geographische Extrempunkte', items: [
|
|
{name:'Nordpol', sub:'90.0°N - Polartag/nacht', lat: 89.99, lon: 0.00},
|
|
{name:'Südpol', sub:'90.0°S - Antarktis', lat:-89.99, lon: 0.00},
|
|
{name:'Ãquator / 0°', sub:'0°N 0°E - Golf v. Guinea',lat: 0.00, lon: 0.00},
|
|
{name:'Ãquator / Borneo', sub:'0°N 109°E', lat: 0.10, lon:109.00},
|
|
{name:'Polarkreis N', sub:'66.56°N', lat: 66.56, lon: 25.00},
|
|
{name:'Polarkreis S', sub:'66.56°S', lat:-66.56, lon: 25.00},
|
|
{name:'Wendekreis Krebs', sub:'23.44°N', lat: 23.44, lon: 0.00},
|
|
{name:'Wendekreis Steinbock', sub:'23.44°S', lat:-23.44, lon: 0.00},
|
|
{name:'Datum-Grenze 180°',sub:'0°N 180°E', lat: 0.00, lon:180.00},
|
|
{name:'Antimeridian', sub:'0°N 179.9°W', lat: 0.00, lon:-179.9},
|
|
{name:'Null-Meridian', sub:'51.5°N 0°E Greenwich', lat: 51.48, lon: 0.00},
|
|
{name:'Pol der Unzug?nglichkeit',sub:'Antarktis, 83.9°S', lat:-83.90, lon: 65.72},
|
|
]},
|
|
{ group: 'Historische & Besondere Orte', items: [
|
|
{name:'Stonehenge', sub:'England, 51.2°N', lat: 51.18, lon: -1.83},
|
|
{name:'Gizeh Pyramiden', sub:'Ãgypten, 30°N', lat: 29.98, lon: 31.13},
|
|
{name:'Angkor Wat', sub:'Kambodscha, 13.4°N', lat: 13.41, lon:103.87},
|
|
{name:'Machu Picchu', sub:'Peru, 13.2°S', lat:-13.16, lon:-72.54},
|
|
{name:'Chichen Itza', sub:'Mexiko, 20.7°N', lat: 20.68, lon:-88.57},
|
|
{name:'Newgrange', sub:'Irland, 53.7°N', lat: 53.69, lon: -6.47},
|
|
{name:'Abu Simbel', sub:'Ãgypten, 22.3°N', lat: 22.34, lon: 31.63},
|
|
{name:'Easter Island', sub:'Pazifik, 27.1°S', lat:-27.12, lon:-109.37},
|
|
{name:'Uluru (Ayers Rock)',sub:'Australien, 25.3°S', lat:-25.34, lon:131.04},
|
|
{name:'Nasca-Linien', sub:'Peru, 14.7°S', lat:-14.72, lon:-75.13},
|
|
]},
|
|
{ group: 'Raumfahrt & Wissenschaft', items: [
|
|
{name:'Cape Canaveral', sub:'Florida, 28.5°N', lat: 28.47, lon:-80.56},
|
|
{name:'Baikonur', sub:'Kasachstan, 46°N', lat: 45.96, lon: 63.31},
|
|
{name:'Kourou', sub:'Franz.-Guayana, 5.3°N',lat: 5.17, lon:-52.68},
|
|
{name:'CERN Genf', sub:'Schweiz, 46.2°N', lat: 46.23, lon: 6.05},
|
|
{name:'ISS-Bahn (0°N)', sub:'Ãquatorebene 51.6° i', lat: 0.00, lon: 0.00},
|
|
{name:'McMurdo Station', sub:'Antarktis, 77.8°S', lat:-77.85, lon:166.67},
|
|
{name:'Amundsen-Scott SP',sub:'Südpol, 90°S', lat:-89.99, lon: 0.00},
|
|
{name:'Alert Kanada', sub:'82.5°N, noerdlichste Siedlung',lat:82.50,lon:-62.33},
|
|
]},
|
|
];
|
|
|
|
//
|
|
const ALL_LOCS = LOC_GROUPS.flatMap(g=>g.items);
|
|
|
|
//
|
|
const locGroupsEl = document.getElementById('locGroups');
|
|
LOC_GROUPS.forEach(g=>{
|
|
const section = document.createElement('div');
|
|
section.className = 'loc-group';
|
|
section.innerHTML = `<div class="loc-group-title">${g.group}</div>`;
|
|
const grid = document.createElement('div');
|
|
grid.className = 'loc-grid';
|
|
g.items.forEach(item=>{
|
|
const idx = ALL_LOCS.indexOf(item);
|
|
const b = document.createElement('button');
|
|
b.className = 'city-btn';
|
|
b.dataset.idx = idx;
|
|
b.dataset.name = item.name.toLowerCase();
|
|
b.dataset.sub = (item.sub||'').toLowerCase();
|
|
b.innerHTML = `${item.name}<span class="loc-sub">${item.sub||''}</span>`;
|
|
b.onclick = ()=>selectLoc(idx);
|
|
grid.appendChild(b);
|
|
});
|
|
section.appendChild(grid);
|
|
locGroupsEl.appendChild(section);
|
|
});
|
|
|
|
function filterLocs(q){
|
|
q=q.toLowerCase();
|
|
document.querySelectorAll('.city-btn').forEach(b=>{
|
|
const match=!q||b.dataset.name.includes(q)||b.dataset.sub.includes(q);
|
|
b.classList.toggle('hidden',!match);
|
|
});
|
|
document.querySelectorAll('.loc-group').forEach(g=>{
|
|
const anyVisible=[...g.querySelectorAll('.city-btn')].some(b=>!b.classList.contains('hidden'));
|
|
g.style.display=anyVisible?'':'none';
|
|
});
|
|
}
|
|
|
|
//
|
|
let loc={name:'Berlin',lat:52.52,lon:13.40};
|
|
let currentDate=new Date(); currentDate.setUTCHours(12,0,0,0);
|
|
let sliderFrac=0.5;
|
|
let sunData=null;
|
|
let viewMode='allsky'; // 'allsky' | 'horizon'
|
|
function setView(v){
|
|
viewMode=v;
|
|
document.getElementById('vtb_allsky').classList.toggle('active',v==='allsky');
|
|
document.getElementById('vtb_horizon').classList.toggle('active',v==='horizon');
|
|
drawArc();
|
|
}
|
|
function toISO(d){return d.getUTCFullYear()+'-'+String(d.getUTCMonth()+1).padStart(2,'0')+'-'+String(d.getUTCDate()).padStart(2,'0');}
|
|
|
|
//
|
|
const OVERLAYS={
|
|
sommer: {active:false, color:'rgba(255,100,30,', label:'Sommersonnenwende', month:6, day:21},
|
|
winter: {active:false, color:'rgba(60,130,255,', label:'Wintersonnenwende', month:12, day:21},
|
|
fruehjahr: {active:false, color:'rgba(50,210,110,', label:'Tag-/Nachtgleiche', month:3, day:20},
|
|
|
|
};
|
|
function toggleOverlay(key){
|
|
OVERLAYS[key].active=!OVERLAYS[key].active;
|
|
const btn=document.getElementById('btn_'+key);
|
|
btn.dataset.active=OVERLAYS[key].active?'1':'0';
|
|
drawArc();
|
|
}
|
|
function clearOverlays(){
|
|
Object.keys(OVERLAYS).forEach(k=>{
|
|
OVERLAYS[k].active=false;
|
|
document.getElementById('btn_'+k).dataset.active='0';
|
|
});
|
|
drawArc();
|
|
}
|
|
function overlayDate(key){
|
|
const o=OVERLAYS[key];
|
|
const y=currentDate.getUTCFullYear();
|
|
return new Date(Date.UTC(y,o.month-1,o.day,12,0,0));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
function openModal(){document.getElementById('modalOverlay').classList.add('vis');}
|
|
function closeModal(){document.getElementById('modalOverlay').classList.remove('vis');}
|
|
document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target===e.currentTarget)closeModal();});
|
|
function selectLoc(i){
|
|
const c=ALL_LOCS[i];
|
|
loc={name:c.name,lat:c.lat,lon:c.lon};
|
|
document.querySelectorAll('.city-btn').forEach(b=>b.classList.toggle('on',parseInt(b.dataset.idx)===i));
|
|
document.getElementById('inputLat').value=c.lat;
|
|
document.getElementById('inputLon').value=c.lon;
|
|
document.getElementById('inputName').value='';
|
|
closeModal(); update();
|
|
}
|
|
function applyCustomCoords(){
|
|
const lat=parseFloat(document.getElementById('inputLat').value);
|
|
const lon=parseFloat(document.getElementById('inputLon').value);
|
|
const name=document.getElementById('inputName').value.trim()||`${lat.toFixed(2)}°, ${lon.toFixed(2)}°`;
|
|
if(isNaN(lat)||isNaN(lon))return;
|
|
loc={name,lat,lon};
|
|
document.querySelectorAll('.city-btn').forEach(b=>b.classList.remove('on'));
|
|
closeModal(); update();
|
|
}
|
|
|
|
//
|
|
function hourToDate(baseDate, utcHour){
|
|
// baseDate: JS Date used only fuer Y/M/D (UTC)
|
|
const d=new Date(baseDate);
|
|
d.setUTCHours(0,0,0,0);
|
|
d.setTime(d.getTime()+utcHour*3600000);
|
|
return d;
|
|
}
|
|
|
|
function sunAltAz(lat,lon,jsDate){
|
|
const obs=new Astronomy.Observer(lat,lon,0);
|
|
const eq=Astronomy.Equator(Astronomy.Body.Sun,jsDate,obs,true,true);
|
|
const hz=Astronomy.Horizon(jsDate,obs,eq.ra,eq.dec,'normal');
|
|
return {altitude:hz.altitude, azimuth:hz.azimuth};
|
|
}
|
|
|
|
function computeSunData(lat,lon,date){
|
|
const obs=new Astronomy.Observer(lat,lon,0);
|
|
// Always search from UTC midnight of the selected day
|
|
const midnight=new Date(date); midnight.setUTCHours(0,0,0,0);
|
|
|
|
let riseUTC=null, setUTC=null, noonUTC=12;
|
|
let polarDay=false, polarNight=false;
|
|
|
|
// Solar transit (noon) - search fuerward from midnight
|
|
try{
|
|
const tr=Astronomy.SearchHourAngle(Astronomy.Body.Sun,obs,0,midnight,+1);
|
|
if(tr){
|
|
const td=tr.time.date;
|
|
noonUTC=td.getUTCHours()+td.getUTCMinutes()/60+td.getUTCSeconds()/3600;
|
|
}
|
|
}catch(e){ console.warn('noon search error',e); }
|
|
|
|
// Sunrise: search from midnight fuerward within 1 day
|
|
try{
|
|
const r=Astronomy.SearchRiseSet(Astronomy.Body.Sun,obs,+1,midnight,1,0);
|
|
if(r){const rd=r.date; riseUTC=rd.getUTCHours()+rd.getUTCMinutes()/60+rd.getUTCSeconds()/3600;}
|
|
}catch(e){ console.warn('rise search error',e); }
|
|
|
|
// Sunset: search from midnight fuerward within 1 day
|
|
try{
|
|
const s=Astronomy.SearchRiseSet(Astronomy.Body.Sun,obs,-1,midnight,1,0);
|
|
if(s){const sd2=s.date; setUTC=sd2.getUTCHours()+sd2.getUTCMinutes()/60+sd2.getUTCSeconds()/3600;}
|
|
}catch(e){ console.warn('set search error',e); }
|
|
|
|
// Polar cases: rise or set not found during the day
|
|
if(riseUTC===null||setUTC===null){
|
|
const noonPos=sunAltAz(lat,lon,hourToDate(date,noonUTC));
|
|
polarDay = noonPos.altitude>0;
|
|
polarNight = !polarDay;
|
|
riseUTC=0; setUTC=24;
|
|
}
|
|
|
|
// Fix rare midnight-crossing (e.g. very high latitudes in summer)
|
|
if(!polarDay&&!polarNight&&setUTC<=riseUTC) setUTC+=24;
|
|
|
|
// Declination at solar noon
|
|
let decl=null;
|
|
try{
|
|
const eq=Astronomy.Equator(Astronomy.Body.Sun,hourToDate(date,noonUTC),obs,true,true);
|
|
decl=eq.dec;
|
|
}catch(e){}
|
|
|
|
// Max elevation at transit
|
|
const maxPos=sunAltAz(lat,lon,hourToDate(date,noonUTC));
|
|
const maxElev=maxPos.altitude;
|
|
|
|
// Rise azimuth
|
|
let riseAz=null;
|
|
if(!polarDay&&!polarNight&&riseUTC!==null){
|
|
riseAz=sunAltAz(lat,lon,hourToDate(date,riseUTC)).azimuth;
|
|
}
|
|
|
|
return{riseUTC,setUTC,noonUTC,polarDay,polarNight,decl,maxElev,riseAz};
|
|
}
|
|
|
|
function fmtH(h){
|
|
if(h===null||isNaN(h))return'-';
|
|
h=((h%24)+24)%24;
|
|
const hh=Math.floor(h), mm=Math.round((h-hh)*60);
|
|
if(mm===60)return String((hh+1)%24).padStart(2,'0')+':00';
|
|
return String(hh).padStart(2,'0')+':'+String(mm).padStart(2,'0');
|
|
}
|
|
//
|
|
//
|
|
function drawWrappedPath(ctx, pts, W, closePath){
|
|
if(!pts.length)return;
|
|
ctx.beginPath();
|
|
let penDown=false;
|
|
for(let i=0;i<pts.length;i++){
|
|
const p=pts[i];
|
|
if(i===0){ctx.moveTo(p.x,p.y);penDown=true;continue;}
|
|
const prev=pts[i-1];
|
|
if(Math.abs(p.x-prev.x)>W*0.5){
|
|
// Wrap detected - start new sub-path
|
|
ctx.moveTo(p.x,p.y);
|
|
} else {
|
|
ctx.lineTo(p.x,p.y);
|
|
}
|
|
}
|
|
if(closePath)ctx.closePath();
|
|
}
|
|
|
|
function azDir(az){
|
|
return['N','NNO','NO','ONO','O','OSO','SO','SSO','S','SSW','SW','WSW','W','WNW','NW','NNW'][Math.round(az/22.5)%16];
|
|
}
|
|
|
|
//
|
|
const arcCanvas=document.getElementById('arcCanvas');
|
|
const ctx=arcCanvas.getContext('2d');
|
|
function resizeCanvas(){arcCanvas.width=arcCanvas.offsetWidth*devicePixelRatio;arcCanvas.height=arcCanvas.offsetHeight*devicePixelRatio;drawArc();}
|
|
|
|
function drawArc(){
|
|
if(!sunData)return;
|
|
const W=arcCanvas.width,H=arcCanvas.height,dpr=devicePixelRatio;
|
|
ctx.clearRect(0,0,W,H);
|
|
const sd=sunData;
|
|
const nPoints=500;
|
|
const isSH=loc.lat<0; //
|
|
|
|
// â•Ââ• PROJEKTION â•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Ââ•Â
|
|
// Gemeinsame Geometrie-Konstanten (werden je nach Modus ?berschrieben)
|
|
let cx,cy,R,groundY,rX,rY,clipFn,proj;
|
|
|
|
if(viewMode==='allsky'){
|
|
// ââ€â‚¬Ã¢â€â‚¬ ALL-SKY ZENITH-DRAUFSICHT ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
// Beobachter schaut nach oben. N=oben, S=unten, O=links, W=rechts
|
|
const pad=52*dpr;
|
|
R=Math.min(W,H)*0.5-pad;
|
|
cx=W*0.5; cy=H*0.5;
|
|
proj=(alt,az)=>{
|
|
const ar=az*Math.PI/180,er=alt*Math.PI/180;
|
|
const r=Math.cos(er)*R;
|
|
return{x:cx-Math.sin(ar)*r, y:cy-Math.cos(ar)*r};
|
|
};
|
|
// Hintergrund
|
|
ctx.fillStyle='#04060f';ctx.fillRect(0,0,W,H);
|
|
const skyG=ctx.createRadialGradient(cx,cy,0,cx,cy,R);
|
|
skyG.addColorStop(0,'#0e1535');skyG.addColorStop(0.7,'#080d28');skyG.addColorStop(1,'#040816');
|
|
ctx.save();ctx.beginPath();ctx.arc(cx,cy,R,0,Math.PI*2);ctx.clip();
|
|
ctx.fillStyle=skyG;ctx.fillRect(0,0,W,H);ctx.restore();
|
|
// Horizont-Ring
|
|
ctx.save();
|
|
ctx.beginPath();ctx.arc(cx,cy,R,0,Math.PI*2);
|
|
ctx.strokeStyle='rgba(200,168,75,.30)';ctx.lineWidth=1.5*dpr;ctx.stroke();
|
|
ctx.beginPath();ctx.rect(0,0,W,H);ctx.arc(cx,cy,R,0,Math.PI*2,true);
|
|
ctx.fillStyle='rgba(5,10,25,0.85)';ctx.fill();ctx.restore();
|
|
// Raster
|
|
[15,30,45,60,75].forEach(el=>{
|
|
const r2=Math.cos(el*Math.PI/180)*R;
|
|
ctx.beginPath();ctx.arc(cx,cy,r2,0,Math.PI*2);
|
|
ctx.strokeStyle='rgba(200,168,75,.06)';ctx.lineWidth=1*dpr;
|
|
ctx.setLineDash([4*dpr,8*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
ctx.font=`${8*dpr}px Space Mono`;ctx.fillStyle='rgba(200,168,75,.30)';ctx.textAlign='left';
|
|
ctx.fillText(el+'°',cx+r2+3*dpr,cy+4*dpr);
|
|
});
|
|
[0,45,90,135,180,225,270,315].forEach(az=>{
|
|
const p0=proj(0,az),p1=proj(89,az);
|
|
ctx.beginPath();ctx.moveTo(p0.x,p0.y);ctx.lineTo(p1.x,p1.y);
|
|
ctx.strokeStyle='rgba(200,168,75,.05)';ctx.lineWidth=1*dpr;ctx.stroke();
|
|
});
|
|
// Achsenkreuz
|
|
ctx.beginPath();
|
|
ctx.moveTo(cx,cy-R);ctx.lineTo(cx,cy+R);
|
|
ctx.moveTo(cx-R,cy);ctx.lineTo(cx+R,cy);
|
|
ctx.strokeStyle='rgba(200,168,75,.07)';ctx.lineWidth=1*dpr;ctx.stroke();
|
|
// Himmelsrichtungen
|
|
const cf=`bold ${11*dpr}px Orbitron`, cc='rgba(200,168,75,.70)';
|
|
ctx.font=cf;ctx.fillStyle=cc;
|
|
ctx.textAlign='center';ctx.textBaseline='bottom';ctx.fillText('N',cx,cy-R-4*dpr);
|
|
ctx.textBaseline='top';ctx.fillText('S',cx,cy+R+4*dpr);
|
|
ctx.textAlign='right';ctx.textBaseline='middle';ctx.fillText('O',cx-R-4*dpr,cy);
|
|
ctx.textAlign='left';ctx.fillText('W',cx+R+4*dpr,cy);
|
|
ctx.textBaseline='alphabetic';
|
|
// Zenit
|
|
ctx.beginPath();ctx.arc(cx,cy,3*dpr,0,Math.PI*2);ctx.fillStyle='rgba(200,168,75,.25)';ctx.fill();
|
|
ctx.font=`${8*dpr}px Orbitron`;ctx.fillStyle='rgba(200,168,75,.35)';
|
|
ctx.textAlign='center';ctx.fillText('Z',cx,cy-6*dpr);
|
|
clipFn=()=>{ctx.beginPath();ctx.arc(cx,cy,R+2*dpr,0,Math.PI*2);ctx.clip();};
|
|
|
|
}else{
|
|
// ââ€â‚¬Ã¢â€â‚¬ HORIZONT-PANORAMA (zylindrisch, 360?) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
// Vollstaendige Rundumsicht: az=0(N) links, az=180(S) Mitte, az=360(N) rechts
|
|
// NH: Sommerbogen von NO (links-Mitte) ?ber S (Mitte-oben) nach NW (rechts-Mitte)
|
|
// SH: az verschoben um 180? ? N Mitte, S an Raendern
|
|
const vPad=36*dpr;
|
|
rY=(H-vPad*2)*0.80;
|
|
cx=W*0.5; groundY=H*0.75;
|
|
R=W*0.5; rX=W*0.5;
|
|
|
|
proj=(alt,az)=>{
|
|
const normAz=isSH?(az+180)%360:az;
|
|
return{ x:(normAz/360)*W, y:groundY-Math.sin(alt*Math.PI/180)*rY };
|
|
};
|
|
|
|
// Himmel
|
|
const skyG=ctx.createLinearGradient(0,0,0,groundY);
|
|
skyG.addColorStop(0,'#020210');skyG.addColorStop(0.4,'#060820');skyG.addColorStop(1,'#0b1230');
|
|
ctx.fillStyle=skyG;ctx.fillRect(0,0,W,groundY);
|
|
const gndG=ctx.createLinearGradient(0,groundY,0,H);
|
|
gndG.addColorStop(0,'#0a1428');gndG.addColorStop(1,'#060c1c');
|
|
ctx.fillStyle=gndG;ctx.fillRect(0,groundY,W,H-groundY);
|
|
ctx.beginPath();ctx.moveTo(0,groundY);ctx.lineTo(W,groundY);
|
|
ctx.strokeStyle='rgba(200,168,75,.30)';ctx.lineWidth=1.5*dpr;ctx.stroke();
|
|
|
|
// Elevation-Gitter
|
|
[15,30,45,60,75].forEach(el=>{
|
|
const yEl=groundY-Math.sin(el*Math.PI/180)*rY;
|
|
ctx.beginPath();ctx.moveTo(0,yEl);ctx.lineTo(W,yEl);
|
|
ctx.strokeStyle='rgba(200,168,75,.06)';ctx.lineWidth=1*dpr;
|
|
ctx.setLineDash([4*dpr,8*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
ctx.font=`${8*dpr}px Space Mono`;ctx.fillStyle='rgba(200,168,75,.28)';
|
|
ctx.textAlign='left';ctx.fillText(el+'°',4*dpr,yEl-3*dpr);
|
|
});
|
|
|
|
// Azimut-Gitter + Labels
|
|
const aziLabelsNH={0:'N',45:'NO',90:'O',135:'SO',180:'S',225:'SW',270:'W',315:'NW',360:'N'};
|
|
const aziLabelsSH={0:'S',45:'SW',90:'W',135:'NW',180:'N',225:'NO',270:'O',315:'SO',360:'S'};
|
|
const aziLabels=isSH?aziLabelsSH:aziLabelsNH;
|
|
[0,45,90,135,180,225,270,315,360].forEach(az=>{
|
|
const xAz=(az/360)*W;
|
|
const isMajor=(az%90===0);
|
|
ctx.beginPath();ctx.moveTo(xAz,0);ctx.lineTo(xAz,groundY);
|
|
ctx.strokeStyle=isMajor?'rgba(200,168,75,.12)':'rgba(200,168,75,.04)';
|
|
ctx.lineWidth=isMajor?1.5*dpr:1*dpr;ctx.stroke();
|
|
ctx.font=`bold ${isMajor?11:8}px Orbitron`;
|
|
ctx.fillStyle=isMajor?'rgba(200,168,75,.75)':'rgba(200,168,75,.35)';
|
|
ctx.textAlign='center';ctx.textBaseline='top';
|
|
ctx.fillText(aziLabels[az]||'',xAz,groundY+5*dpr);
|
|
ctx.textBaseline='alphabetic';
|
|
});
|
|
|
|
// Zenit-Strich ?ber S(NH)/N(SH) = Mitte
|
|
ctx.beginPath();ctx.moveTo(W*0.5,groundY);ctx.lineTo(W*0.5,vPad*0.5);
|
|
ctx.strokeStyle='rgba(200,168,75,.08)';ctx.lineWidth=1*dpr;
|
|
ctx.setLineDash([3*dpr,6*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
ctx.font=`${9*dpr}px Orbitron`;ctx.fillStyle='rgba(200,168,75,.35)';
|
|
ctx.textAlign='center';ctx.textBaseline='top';
|
|
ctx.fillText('ZENIT',W*0.5,4*dpr);ctx.textBaseline='alphabetic';
|
|
|
|
clipFn=()=>{ctx.rect(0,0,W,H);};
|
|
|
|
}
|
|
|
|
// ââ€â‚¬Ã¢â€â‚¬ Boegenpunkte bauen ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
const h0=sd.polarDay?0:sd.riseUTC;
|
|
const h1=sd.polarDay?24:sd.setUTC;
|
|
const pts=[];
|
|
for(let i=0;i<=nPoints;i++){
|
|
const h=h0+(h1-h0)*i/nPoints;
|
|
const{altitude,azimuth}=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,h));
|
|
// Nur Punkte ?ber dem Horizont (oder alle bei Polartag)
|
|
if(sd.polarDay||altitude>=-0.5)
|
|
pts.push({...proj(altitude,azimuth),alt:altitude,az:azimuth,h});
|
|
}
|
|
|
|
// ââ€â‚¬Ã¢â€â‚¬ Boegen zeichnen ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
// Clip innerhalb des Himmelskreises fuer saubere Darstellung
|
|
ctx.save();
|
|
clipFn();
|
|
ctx.clip();
|
|
|
|
if(!sd.polarNight){
|
|
// Unter-Horizont gestrichelt (normal day) - nur Punkte wo alt<0
|
|
if(!sd.polarDay){
|
|
const sub=[];
|
|
for(let i=0;i<=80;i++){
|
|
const h=sd.riseUTC-3+i*6/80;
|
|
const{altitude,azimuth}=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,h));
|
|
if(altitude<0) sub.push(proj(altitude,azimuth));
|
|
else if(sub.length>0) { sub.push(null); } // Unterbrecher
|
|
}
|
|
// Zeichne mit Unterbrechungen
|
|
ctx.beginPath();
|
|
let inPath=false;
|
|
sub.forEach(p=>{
|
|
if(!p){inPath=false;return;}
|
|
if(!inPath){ctx.moveTo(p.x,p.y);inPath=true;}
|
|
else ctx.lineTo(p.x,p.y);
|
|
});
|
|
ctx.strokeStyle='rgba(200,168,75,.15)';ctx.lineWidth=1.5*dpr;
|
|
ctx.setLineDash([4*dpr,8*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
}
|
|
|
|
// Äußerer Gluehschein
|
|
ctx.beginPath();
|
|
drawWrappedPath(ctx,pts,W,sd.polarDay);
|
|
ctx.strokeStyle=sd.polarDay?'rgba(120,210,255,.10)':'rgba(255,150,20,.10)';
|
|
ctx.lineWidth=16*dpr;ctx.stroke();
|
|
|
|
// Farbiger Boegen (Segmente)
|
|
for(let i=1;i<pts.length;i++){
|
|
const t=i/pts.length;
|
|
let col;
|
|
if(sd.polarDay){
|
|
col='rgba(160,220,255,.80)';
|
|
}else{
|
|
const g=t<.5?Math.floor(60+t*380):Math.floor(440-t*380);
|
|
col=`rgba(255,${Math.max(0,Math.min(255,g))},20,.90)`;
|
|
}
|
|
if(Math.abs(pts[i].x-pts[i-1].x)<W*0.5){
|
|
ctx.beginPath();ctx.moveTo(pts[i-1].x,pts[i-1].y);ctx.lineTo(pts[i].x,pts[i].y);
|
|
ctx.strokeStyle=col;ctx.lineWidth=3*dpr;ctx.stroke();
|
|
}
|
|
}
|
|
|
|
// Mittag-Marker
|
|
const noonPt=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,sd.noonUTC));
|
|
const np=proj(noonPt.altitude,noonPt.azimuth);
|
|
ctx.beginPath();ctx.arc(np.x,np.y,5*dpr,0,Math.PI*2);ctx.fillStyle='rgba(200,168,75,.7)';ctx.fill();
|
|
// Linie zum Zenit
|
|
ctx.beginPath();ctx.moveTo(np.x,np.y);ctx.lineTo(cx,cy);
|
|
ctx.strokeStyle='rgba(200,168,75,.12)';ctx.lineWidth=1*dpr;ctx.setLineDash([3*dpr,6*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
ctx.font=`bold ${9*dpr}px Orbitron`;ctx.fillStyle='rgba(200,168,75,.85)';ctx.textAlign='center';
|
|
// Label außerhalb nahe dem Punkt, leicht versetzt
|
|
const nAngle=Math.atan2(np.x-cx,np.y-cy); // Winkel vom Zenit
|
|
ctx.fillText('MITTAG '+fmtH(sd.noonUTC), np.x+Math.sin(nAngle)*14*dpr, np.y+Math.cos(nAngle)*14*dpr);
|
|
|
|
// Aufgang / Untergang
|
|
if(!sd.polarDay){
|
|
const rP=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,sd.riseUTC));
|
|
const rp=proj(0,rP.azimuth);
|
|
ctx.beginPath();ctx.arc(rp.x,rp.y,6*dpr,0,Math.PI*2);ctx.fillStyle='#ff8040';ctx.fill();
|
|
ctx.font=`bold ${9*dpr}px Orbitron`;ctx.fillStyle='rgba(255,165,60,.95)';
|
|
// Label radial nach außen
|
|
const rAng=Math.atan2(rp.x-cx,rp.y-cy);
|
|
const lx=rp.x+Math.sin(rAng)*20*dpr, ly=rp.y+Math.cos(rAng)*20*dpr;
|
|
ctx.textAlign=rp.x<cx?'right':'left';
|
|
ctx.fillText('↑ '+fmtH(sd.riseUTC),lx,ly);
|
|
|
|
const sP=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,sd.setUTC));
|
|
const sp=proj(0,sP.azimuth);
|
|
ctx.beginPath();ctx.arc(sp.x,sp.y,6*dpr,0,Math.PI*2);ctx.fillStyle='#ff5010';ctx.fill();
|
|
ctx.font=`bold ${9*dpr}px Orbitron`;ctx.fillStyle='rgba(255,130,40,.95)';
|
|
const sAng=Math.atan2(sp.x-cx,sp.y-cy);
|
|
const sx2=sp.x+Math.sin(sAng)*20*dpr, sy2=sp.y+Math.cos(sAng)*20*dpr;
|
|
ctx.textAlign=sp.x>cx?'left':'right';
|
|
ctx.fillText(fmtH(sd.setUTC)+' ↓',sx2,sy2);
|
|
}
|
|
|
|
if(sd.polarDay){
|
|
ctx.font=`bold ${11*dpr}px Orbitron`;ctx.fillStyle='rgba(140,210,255,.8)';ctx.textAlign='center';
|
|
ctx.fillText('POLARTAG · MITTERNACHTSSONNE',cx,cy+R-20*dpr);
|
|
}
|
|
}else{
|
|
// Polarnacht: Boegen unter dem Horizont gestrichelt
|
|
ctx.beginPath();drawWrappedPath(ctx,pts,W,true);
|
|
ctx.strokeStyle='rgba(80,120,200,.25)';ctx.lineWidth=2*dpr;
|
|
ctx.setLineDash([6*dpr,10*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
ctx.font=`bold ${11*dpr}px Orbitron`;ctx.fillStyle='rgba(100,150,230,.65)';ctx.textAlign='center';
|
|
ctx.fillText('POLARNACHT',cx,cy+20*dpr);
|
|
}
|
|
|
|
ctx.restore(); // end clip
|
|
|
|
// ââ€â‚¬Ã¢â€â‚¬ Jahres-?berlagerungs-Boegen ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
Object.keys(OVERLAYS).forEach(key=>{
|
|
const ov=OVERLAYS[key];
|
|
if(!ov.active)return;
|
|
const oDate=overlayDate(key);
|
|
const oSd=computeSunData(loc.lat,loc.lon,oDate);
|
|
const oh0=oSd.polarDay?0:oSd.riseUTC;
|
|
const oh1=oSd.polarDay?24:oSd.setUTC;
|
|
const opts=[];
|
|
for(let i=0;i<=500;i++){
|
|
const h=oh0+(oh1-oh0)*i/500;
|
|
const{altitude,azimuth}=sunAltAz(loc.lat,loc.lon,hourToDate(oDate,h));
|
|
if(oSd.polarDay||altitude>=-0.5)
|
|
opts.push(proj(altitude,azimuth));
|
|
}
|
|
// Gluehhalo + Boegen
|
|
ctx.save();
|
|
clipFn(); ctx.clip();
|
|
ctx.beginPath();
|
|
drawWrappedPath(ctx,opts,W,oSd.polarDay);
|
|
ctx.strokeStyle=ov.color+'0.12)';ctx.lineWidth=10*dpr;ctx.stroke();
|
|
ctx.beginPath();
|
|
drawWrappedPath(ctx,opts,W,oSd.polarDay);
|
|
ctx.strokeStyle=ov.color+'0.65)';ctx.lineWidth=2*dpr;
|
|
ctx.setLineDash([6*dpr,4*dpr]);ctx.stroke();ctx.setLineDash([]);
|
|
// Label
|
|
if(!oSd.polarNight&&!oSd.polarDay){
|
|
const midIdx=Math.floor(opts.length/2);
|
|
const mp=opts[midIdx];
|
|
ctx.font=`bold ${9*dpr}px Orbitron`;
|
|
ctx.fillStyle=ov.color+'0.90)';ctx.textAlign='center';
|
|
if(viewMode==='allsky'){
|
|
const ang=Math.atan2(mp.x-cx,mp.y-cy);
|
|
ctx.fillText(ov.label,mp.x+Math.sin(ang)*18*dpr,mp.y+Math.cos(ang)*18*dpr);
|
|
}else{
|
|
ctx.fillText(ov.label,mp.x,mp.y-14*dpr);
|
|
}
|
|
}
|
|
// Aufgangs-/Untergangs-Dots
|
|
if(!oSd.polarDay){
|
|
const rPos=sunAltAz(loc.lat,loc.lon,hourToDate(oDate,oSd.riseUTC));
|
|
const rp=proj(0,rPos.azimuth);
|
|
ctx.beginPath();ctx.arc(rp.x,rp.y,4*dpr,0,Math.PI*2);
|
|
ctx.fillStyle=ov.color+'0.80)';ctx.fill();
|
|
const sPos=sunAltAz(loc.lat,loc.lon,hourToDate(oDate,oSd.setUTC));
|
|
const sp=proj(0,sPos.azimuth);
|
|
ctx.beginPath();ctx.arc(sp.x,sp.y,4*dpr,0,Math.PI*2);
|
|
ctx.fillStyle=ov.color+'0.80)';ctx.fill();
|
|
}
|
|
ctx.restore();
|
|
|
|
});
|
|
|
|
// ââ€â‚¬Ã¢â€â‚¬ Aktuelle Sonnenposition ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
const curH=sd.riseUTC+sliderFrac*(sd.setUTC-sd.riseUTC);
|
|
const sunPos=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,curH));
|
|
const sp2=proj(sunPos.altitude,sunPos.azimuth);
|
|
const below=sunPos.altitude<0;
|
|
|
|
// Linie Sonne ? Zenit
|
|
ctx.beginPath();ctx.moveTo(sp2.x,sp2.y);ctx.lineTo(cx,cy);
|
|
ctx.strokeStyle='rgba(255,220,60,.07)';ctx.lineWidth=1*dpr;ctx.stroke();
|
|
|
|
// Gluehkraenze
|
|
[80,45,22,11].forEach((r,i)=>{
|
|
const a=[.03,.08,.16,.42][i]*(below ? .10 : 1);
|
|
const g2=ctx.createRadialGradient(sp2.x,sp2.y,0,sp2.x,sp2.y,r*dpr);
|
|
g2.addColorStop(0,`rgba(255,240,120,${a})`);g2.addColorStop(1,'rgba(255,200,60,0)');
|
|
ctx.beginPath();ctx.arc(sp2.x,sp2.y,r*dpr,0,Math.PI*2);ctx.fillStyle=g2;ctx.fill();
|
|
});
|
|
|
|
// Sonnenscheibe
|
|
const discR=below?5*dpr:10*dpr;
|
|
const sg=ctx.createRadialGradient(sp2.x-2*dpr,sp2.y-2*dpr,0,sp2.x,sp2.y,discR);
|
|
sg.addColorStop(0,below?'#ffe0b0':'#fffce0');
|
|
sg.addColorStop(.4,below?'#ff9040':'#ffd860');
|
|
sg.addColorStop(1,'#ff7010');
|
|
ctx.globalAlpha=below ? .18 : 1;
|
|
ctx.beginPath();ctx.arc(sp2.x,sp2.y,discR,0,Math.PI*2);ctx.fillStyle=sg;ctx.fill();
|
|
ctx.globalAlpha=1;
|
|
|
|
// ââ€â‚¬Ã¢â€â‚¬ Stats ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
document.getElementById('statElev').textContent=sunPos.altitude.toFixed(1)+'°';
|
|
document.getElementById('statAz').textContent=sunPos.azimuth.toFixed(1)+'°';
|
|
document.getElementById('statDir').textContent=azDir(sunPos.azimuth);
|
|
const pct=sliderFrac*100;
|
|
document.getElementById('phaseBar').style.width=pct+'%';
|
|
document.getElementById('statPhase').textContent=
|
|
sd.polarNight?'Polarnacht':sd.polarDay?'Polartag':
|
|
pct<10?'Morgenr?te':pct<35?'Vormittag':pct<55?'Sonnenmittag':pct<82?'Nachmittag':'Abendd?mmerung';
|
|
const hh=((Math.floor(curH)%24)+24)%24;
|
|
const mm=Math.round((curH-Math.floor(curH))*60)%60;
|
|
const ts=String(hh).padStart(2,'0')+':'+String(mm).padStart(2,'0');
|
|
document.getElementById('timeBig').textContent=ts;
|
|
document.getElementById('sliderTimeLabel').textContent=ts;
|
|
}
|
|
|
|
function onSlider(){sliderFrac=parseInt(document.getElementById('timeSlider').value)/1000;drawArc();}
|
|
|
|
function buildTicks(){
|
|
if(!sunData)return;
|
|
const sd=sunData;
|
|
document.getElementById('timeTicks').innerHTML='';
|
|
const h0=sd.riseUTC,h1=sd.setUTC;
|
|
for(let i=0;i<=6;i++){
|
|
const d=document.createElement('div');
|
|
d.className='tick';
|
|
d.textContent=fmtH(h0+(h1-h0)*i/6);
|
|
document.getElementById('timeTicks').appendChild(d);
|
|
}
|
|
document.getElementById('lbl_rise').textContent=sd.polarDay?'00:00':fmtH(sd.riseUTC);
|
|
document.getElementById('lbl_noon').textContent='| '+fmtH(sd.noonUTC);
|
|
document.getElementById('lbl_set').textContent=sd.polarDay?'24:00':fmtH(sd.setUTC);
|
|
document.getElementById('sliderLabel').textContent=
|
|
sd.polarDay?'POLARTAG - GANZER TAG':sd.polarNight?'POLARNACHT':
|
|
'TAGESVERLAUF · '+fmtH(sd.riseUTC)+' - '+fmtH(sd.setUTC)+' UTC';
|
|
}
|
|
|
|
function update(){
|
|
dateInput.value=toISO(currentDate);
|
|
document.getElementById('infoName').textContent=loc.name;
|
|
document.getElementById('infoLat').textContent=loc.lat.toFixed(2)+'°';
|
|
document.getElementById('infoLon').textContent=loc.lon.toFixed(2)+'°';
|
|
document.getElementById('locBtnText').textContent=loc.name.toUpperCase().slice(0,14);
|
|
|
|
sunData=computeSunData(loc.lat,loc.lon,currentDate);
|
|
const sd=sunData;
|
|
|
|
if(!sd.polarDay&&!sd.polarNight){
|
|
document.getElementById('statRise').textContent=fmtH(sd.riseUTC);
|
|
document.getElementById('statSet').textContent=fmtH(sd.setUTC);
|
|
document.getElementById('statNoon').textContent=fmtH(sd.noonUTC);
|
|
const dur=sd.setUTC-sd.riseUTC;
|
|
document.getElementById('statDay').textContent=Math.floor(dur)+'h '+Math.round((dur%1)*60)+'m';
|
|
}else{
|
|
document.getElementById('statRise').textContent=sd.polarDay?'ganztags':'-';
|
|
document.getElementById('statSet').textContent=sd.polarDay?'ganztags':'-';
|
|
document.getElementById('statNoon').textContent=fmtH(sd.noonUTC);
|
|
document.getElementById('statDay').textContent=sd.polarDay?'24h 0m':'0h 0m';
|
|
}
|
|
|
|
document.getElementById('statDecl').textContent=sd.decl!==null?sd.decl.toFixed(2)+'°':'-°';
|
|
document.getElementById('statMaxElev').textContent=sd.maxElev!==null?sd.maxElev.toFixed(1)+'°':'-°';
|
|
document.getElementById('statRiseAz').textContent=sd.riseAz!==null?sd.riseAz.toFixed(1)+'°':'-°';
|
|
|
|
const curH=sd.riseUTC+sliderFrac*(sd.setUTC-sd.riseUTC);
|
|
const sunPos=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,curH));
|
|
document.getElementById('statElev').textContent=sunPos.altitude.toFixed(1)+'°';
|
|
document.getElementById('statAz').textContent=sunPos.azimuth.toFixed(1)+'°';
|
|
document.getElementById('statDir').textContent=azDir(sunPos.azimuth);
|
|
|
|
const pct=sliderFrac*100;
|
|
document.getElementById('phaseBar').style.width=pct+'%';
|
|
document.getElementById('statPhase').textContent=
|
|
sd.polarNight?'Polarnacht':sd.polarDay?'Polartag':
|
|
pct<10?'Morgenr?te':pct<35?'Vormittag':pct<55?'Sonnenmittag':pct<82?'Nachmittag':'Abendd?mmerung';
|
|
|
|
const hh=((Math.floor(curH)%24)+24)%24;
|
|
const mm=Math.round((curH-Math.floor(curH))*60)%60;
|
|
const ts=String(hh).padStart(2,'0')+':'+String(mm).padStart(2,'0');
|
|
document.getElementById('timeBig').textContent=ts;
|
|
document.getElementById('sliderTimeLabel').textContent=ts;
|
|
|
|
buildTicks();
|
|
drawArc();
|
|
updateOverlayTooltips();
|
|
}
|
|
|
|
function updateOverlayTooltips(){
|
|
const year=currentDate.getUTCFullYear();
|
|
[{key:'sommer',month:6,day:21},{key:'winter',month:12,day:21},{key:'fruehjahr',month:3,day:20}]
|
|
.forEach(({key,month,day})=>{
|
|
const oDate=new Date(Date.UTC(year,month-1,day,12,0,0));
|
|
const sd=computeSunData(loc.lat,loc.lon,oDate);
|
|
const dateStr=String(day).padStart(2,'0')+'.'+String(month).padStart(2,'0')+'.'+year;
|
|
document.getElementById('tip_'+key+'_date').textContent=dateStr;
|
|
if(sd.polarDay){
|
|
['rise','set','elev','az'].forEach(f=>document.getElementById('tip_'+key+'_'+f).textContent='-');
|
|
document.getElementById('tip_'+key+'_dur').textContent='24h 0m (Polartag)';
|
|
} else if(sd.polarNight){
|
|
['rise','set','elev','az'].forEach(f=>document.getElementById('tip_'+key+'_'+f).textContent='-');
|
|
document.getElementById('tip_'+key+'_dur').textContent='0h 0m (Polarnacht)';
|
|
} else {
|
|
const dur=sd.setUTC-sd.riseUTC;
|
|
document.getElementById('tip_'+key+'_rise').textContent=fmtH(sd.riseUTC)+' UTC';
|
|
document.getElementById('tip_'+key+'_set').textContent=fmtH(sd.setUTC)+' UTC';
|
|
document.getElementById('tip_'+key+'_dur').textContent=Math.floor(dur)+'h '+Math.round((dur%1)*60)+'m';
|
|
document.getElementById('tip_'+key+'_elev').textContent=sd.maxElev.toFixed(1)+'°';
|
|
document.getElementById('tip_'+key+'_az').textContent=sd.riseAz.toFixed(1)+'°';
|
|
}
|
|
});
|
|
}
|
|
|
|
//
|
|
let _resizeTimer;
|
|
window.addEventListener('resize',()=>{clearTimeout(_resizeTimer);_resizeTimer=setTimeout(()=>{resizeCanvas();},60);});
|
|
const ro=new ResizeObserver(()=>requestAnimationFrame(resizeCanvas));
|
|
ro.observe(arcCanvas.parentElement);
|
|
|
|
//
|
|
const dateInput=document.getElementById('dateInput');
|
|
dateInput.value=toISO(currentDate);
|
|
dateInput.addEventListener('change',()=>{
|
|
if(!dateInput.value)return;
|
|
currentDate=new Date(dateInput.value+'T12:00:00Z');
|
|
update();
|
|
});
|
|
function shiftDay(n){
|
|
currentDate=new Date(currentDate.getTime()+n*86400000);
|
|
dateInput.value=toISO(currentDate);
|
|
update();
|
|
}
|
|
|
|
//
|
|
function geoLocate(){
|
|
if(!navigator.geolocation){alert('Geolocation wird von diesem Browser nicht unterst?tzt.');return;}
|
|
const btn=document.getElementById('geoBtn');
|
|
const lbl=document.getElementById('geoBtnText');
|
|
btn.classList.add('loading');lbl.textContent='...';
|
|
navigator.geolocation.getCurrentPosition(
|
|
pos=>{
|
|
const lat=Math.round(pos.coords.latitude*10000)/10000;
|
|
const lon=Math.round(pos.coords.longitude*10000)/10000;
|
|
loc={name:'Aktueller Standort',lat,lon};
|
|
document.getElementById('inputLat').value=lat;
|
|
document.getElementById('inputLon').value=lon;
|
|
document.getElementById('inputName').value='Aktueller Standort';
|
|
document.querySelectorAll('.city-btn').forEach(b=>b.classList.remove('on'));
|
|
btn.classList.remove('loading');lbl.textContent='GPS';
|
|
closeModal();update();
|
|
},
|
|
err=>{
|
|
btn.classList.remove('loading');lbl.textContent='GPS';
|
|
alert('Standort konnte nicht ermittelt werden: '+err.message);
|
|
},
|
|
{timeout:10000,enableHighAccuracy:true}
|
|
);
|
|
}
|
|
|
|
//
|
|
//
|
|
selectLoc(0);
|
|
document.getElementById('timeSlider').value=500;
|
|
resizeCanvas();
|
|
update();
|
|
</script>
|
|
</div>
|
|
|
|
<?php require __DIR__ . '/footer.php'; ?>
|