Planeten Wechselt das Sternbild eingebaut
This commit is contained in:
+453
-15
@@ -11,35 +11,99 @@ $publicBasePath = './';
|
||||
$currentYear = (int) date('Y');
|
||||
$selectedYear = isset($_GET['year']) ? (int) $_GET['year'] : $currentYear;
|
||||
$selectedMonth = isset($_GET['month']) ? max(1, min(12, (int) $_GET['month'])) : (int) date('n');
|
||||
// -------------------------------------------------------------------------
|
||||
// Grenzwerte fuer Sternkarte und Objektanzeige
|
||||
// -------------------------------------------------------------------------
|
||||
// Maximale Helligkeit (Magnitude) der Sterne, die aus der Datenbank geladen
|
||||
// werden. Hoehere Werte laden mehr, aber auch schwaechere Sterne.
|
||||
$starDatabaseMagnitudeLimit = 6.0;
|
||||
|
||||
// Maximale Helligkeit der Sterne, die auf der Monats-Sternkarte sichtbar
|
||||
// gezeichnet werden sollen.
|
||||
$starDisplayMagnitudeLimit = 4.4;
|
||||
|
||||
// Sterne bis zu dieser Helligkeit erhalten auf der Sternkarte einen Namen.
|
||||
$starLabelMagnitudeLimit = 2.0;
|
||||
$messierDatabaseMagnitudeLimit = 4.0;
|
||||
|
||||
// Maximale Helligkeit der Messier-Objekte, die aus der Datenbank geladen
|
||||
// werden.
|
||||
$messierDatabaseMagnitudeLimit = 2.0;
|
||||
|
||||
// Messier-Objekte bis zu dieser Helligkeit werden auf der Monatskarte
|
||||
// eingeblendet.
|
||||
$messierDisplayMagnitudeLimit = 1.0;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Grenzwerte fuer Monatsereignisse
|
||||
// -------------------------------------------------------------------------
|
||||
// Maximaler Winkelabstand fuer Begegnungen zwischen Mond und Planeten.
|
||||
$moonPlanetMaxSeparationDeg = 1.5;
|
||||
|
||||
// Maximaler Winkelabstand fuer Begegnungen zwischen Mond und ausgewaehlten
|
||||
// Sternhaufen wie Plejaden oder Praesepe.
|
||||
$moonDeepSkyMaxSeparationDeg = 1.0;
|
||||
$planetBrightStarMaxSeparationDeg = 1.0;
|
||||
|
||||
// Maximaler Winkelabstand fuer Begegnungen zwischen hellen Planeten und
|
||||
// ausgewaehlten hellen Sternen wie Regulus oder Spica.
|
||||
$planetBrightStarMaxSeparationDeg = 1.0;
|
||||
|
||||
// Maximaler Winkelabstand fuer Planet-Planet-Konjunktionen.
|
||||
$planetConjunctionMaxSeparationDeg = 1.0;
|
||||
|
||||
// Nur Sterne bis zu dieser Helligkeit werden fuer Mondbedeckungen
|
||||
// beruecksichtigt.
|
||||
$moonOccultationMaxMag = 2.0;
|
||||
// Gute Merkur-Sichtbarkeit wird nicht nur über Elongation, sondern über echte
|
||||
// Beobachtbarkeit am Himmel definiert: Wir prüfen pro Tag zu einem festen
|
||||
// Zeitpunkt relativ zu Sonnenauf- oder -untergang, ob Merkur hoch genug steht
|
||||
// und die Sonne bereits tief genug unter dem Horizont ist.
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Schwellen fuer "Gute Merkur-Sichtbarkeit"
|
||||
// -------------------------------------------------------------------------
|
||||
// Gute Merkur-Sichtbarkeit wird nicht nur ueber Elongation definiert, sondern
|
||||
// ueber echte Beobachtbarkeit in der Daemmerung.
|
||||
// Geprueft wird pro Tag:
|
||||
// morgens eine feste Zahl Minuten VOR Sonnenaufgang,
|
||||
// abends eine feste Zahl Minuten NACH Sonnenuntergang.
|
||||
$mercuryGoodVisibilitySampleOffsetMinutes = 45;
|
||||
// Morgens: so viele Minuten VOR Sonnenaufgang prüfen.
|
||||
// Abends: so viele Minuten NACH Sonnenuntergang prüfen.
|
||||
|
||||
// Merkur muss zum Pruefzeitpunkt mindestens diese Hoehe ueber dem Horizont
|
||||
// erreichen, damit der Tag als gut sichtbar gilt.
|
||||
$mercuryGoodVisibilityMinAltitudeDeg = 7.0;
|
||||
// Merkur muss bei diesem Prüfzeitpunkt mindestens so hoch über dem Horizont
|
||||
// stehen, damit wir die Sichtbarkeit als "gut" einstufen.
|
||||
|
||||
// Gleichzeitig muss die Sonne ausreichend tief unter dem Horizont stehen.
|
||||
// So vermeiden wir Treffer, bei denen Merkur zwar formal sichtbar waere, aber
|
||||
// noch zu stark in der hellen Daemmerung untergeht.
|
||||
$mercuryGoodVisibilityMaxSunAltitudeDeg = -3.0;
|
||||
// Die Sonne muss gleichzeitig mindestens 4 Grad unter dem Horizont stehen.
|
||||
// So vermeiden wir Treffer, bei denen Merkur zwar formal da ist, aber noch zu
|
||||
// stark in der hellen Dämmerung untergeht.
|
||||
|
||||
// Erst ab dieser Anzahl aufeinanderfolgender guter Tage erzeugen wir ein
|
||||
// Monatsereignis. Einzelne Ausreisser-Tage werden dadurch herausgefiltert.
|
||||
$mercuryGoodVisibilityMinConsecutiveDays = 3;
|
||||
// Einzelne gute Tage sind oft zu zufällig. Erst ab dieser Anzahl
|
||||
// aufeinanderfolgender Tage erzeugen wir ein Monatsereignis.
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Schwellen fuer "Planetenparade"
|
||||
// -------------------------------------------------------------------------
|
||||
// Geprueft wird morgens eine feste Zahl Minuten VOR Sonnenaufgang und abends
|
||||
// eine feste Zahl Minuten NACH Sonnenuntergang.
|
||||
$planetParadeSampleOffsetMinutes = 45;
|
||||
|
||||
// Ein Planet zaehlt fuer die Parade nur dann mit, wenn er zum Pruefzeitpunkt
|
||||
// mindestens diese Hoehe ueber dem Horizont erreicht.
|
||||
$planetParadeMinAltitudeDeg = 7.0;
|
||||
|
||||
// Gleichzeitig muss die Sonne bereits tief genug unter dem Horizont stehen,
|
||||
// damit die Parade nicht nur rechnerisch, sondern auch praktisch sichtbar ist.
|
||||
$planetParadeMaxSunAltitudeDeg = -4.0;
|
||||
|
||||
// Die beteiligten Planeten muessen innerhalb dieses Azimutbogens liegen.
|
||||
// So vermeiden wir Treffer, bei denen die Planeten zu weit ueber den Himmel
|
||||
// verteilt sind und nicht mehr als zusammenhaengende Parade wirken.
|
||||
$planetParadeMaxAzimuthSpanDeg = 120.0;
|
||||
|
||||
// Mindestens so viele helle Planeten muessen gleichzeitig sichtbar sein,
|
||||
// damit wir von einer Planetenparade sprechen.
|
||||
$planetParadeMinPlanetCount = 4;
|
||||
|
||||
// Erst ab dieser Anzahl aufeinanderfolgender Tage erzeugen wir ein Ereignis.
|
||||
// Das filtert kurzlebige Ein-Tages-Treffer heraus.
|
||||
$planetParadeMinConsecutiveDays = 2;
|
||||
|
||||
$years = range($currentYear - 2, $currentYear + 5);
|
||||
$months = [
|
||||
@@ -83,6 +147,8 @@ $moonPlanetApproaches = [];
|
||||
$moonDeepSkyApproaches = [];
|
||||
$planetBrightStarApproaches = [];
|
||||
$mercuryGoodVisibilityEvents = [];
|
||||
$planetParadeEvents = [];
|
||||
$planetConstellationChangeEvents = [];
|
||||
$planetConjunctions = [];
|
||||
$eclipseEvents = [];
|
||||
$moonApsisEvents = [];
|
||||
@@ -203,6 +269,171 @@ if (!function_exists('findMonthForecastSolarLongitudeCrossing')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('buildObservationTipInsertSql')) {
|
||||
function buildObservationTipInsertSql(array $events, DateTimeZone $timezone): string
|
||||
{
|
||||
$rows = [];
|
||||
|
||||
foreach ($events as $event) {
|
||||
if (!is_array($event)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tip = trim((string) ($event['event'] ?? ''));
|
||||
if ($tip === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$moment = null;
|
||||
$localIso = trim((string) ($event['local_iso'] ?? ''));
|
||||
if ($localIso !== '') {
|
||||
try {
|
||||
$moment = new DateTimeImmutable($localIso);
|
||||
} catch (Throwable $e) {
|
||||
$moment = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moment instanceof DateTimeImmutable) {
|
||||
$date = trim((string) ($event['date'] ?? ''));
|
||||
$time = trim((string) ($event['time'] ?? ''));
|
||||
if ($date !== '' && $time !== '') {
|
||||
$moment = DateTimeImmutable::createFromFormat('d.m.Y H:i', $date . ' ' . $time, $timezone) ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moment instanceof DateTimeImmutable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = sprintf(
|
||||
"('%s', '%s')",
|
||||
$moment->setTimezone($timezone)->format('Y-m-d H:i:s'),
|
||||
str_replace("'", "''", $tip)
|
||||
);
|
||||
}
|
||||
|
||||
if ($rows === []) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return "INSERT INTO `astro_sonobs_observationtips` (`moment`, `tip`)\nVALUES\n "
|
||||
. implode(",\n ", $rows)
|
||||
. ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('escapeIcalText')) {
|
||||
function escapeIcalText(string $value): string
|
||||
{
|
||||
return str_replace(
|
||||
["\\", ";", ",", "\r\n", "\n", "\r"],
|
||||
["\\\\", "\\;", "\\,", "\\n", "\\n", "\\n"],
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('foldIcalLine')) {
|
||||
function foldIcalLine(string $line): string
|
||||
{
|
||||
$chunks = preg_split('/(.{1,73})/u', $line, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
if ($chunks === false || $chunks === []) {
|
||||
return $line;
|
||||
}
|
||||
|
||||
$firstChunk = array_shift($chunks);
|
||||
return $firstChunk . ($chunks ? "\r\n " . implode("\r\n ", $chunks) : '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('buildObservationTipCalendarIcs')) {
|
||||
function buildObservationTipCalendarIcs(array $events, DateTimeZone $timezone): string
|
||||
{
|
||||
$lines = [
|
||||
'BEGIN:VCALENDAR',
|
||||
'VERSION:2.0',
|
||||
'PRODID:-//AstroTools//Monatsvorhersage//DE',
|
||||
'CALSCALE:GREGORIAN',
|
||||
'METHOD:PUBLISH',
|
||||
'X-WR-CALNAME:AstroTools Monatsereignisse',
|
||||
];
|
||||
|
||||
$createdUtc = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Ymd\THis\Z');
|
||||
|
||||
foreach ($events as $index => $event) {
|
||||
if (!is_array($event)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$summary = trim((string) ($event['event'] ?? ''));
|
||||
if ($summary === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$moment = null;
|
||||
$localIso = trim((string) ($event['local_iso'] ?? ''));
|
||||
if ($localIso !== '') {
|
||||
try {
|
||||
$moment = new DateTimeImmutable($localIso);
|
||||
} catch (Throwable $e) {
|
||||
$moment = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moment instanceof DateTimeImmutable) {
|
||||
$date = trim((string) ($event['date'] ?? ''));
|
||||
$time = trim((string) ($event['time'] ?? ''));
|
||||
if ($date !== '' && $time !== '') {
|
||||
$moment = DateTimeImmutable::createFromFormat('d.m.Y H:i', $date . ' ' . $time, $timezone) ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moment instanceof DateTimeImmutable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$startUtc = $moment->setTimezone(new DateTimeZone('UTC'));
|
||||
$endMoment = null;
|
||||
$endLocalIso = trim((string) ($event['end_local_iso'] ?? ''));
|
||||
if ($endLocalIso !== '') {
|
||||
try {
|
||||
$endMoment = new DateTimeImmutable($endLocalIso);
|
||||
} catch (Throwable $e) {
|
||||
$endMoment = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$endMoment instanceof DateTimeImmutable) {
|
||||
$durationMinutes = isset($event['duration_minutes']) ? max(1, (int) $event['duration_minutes']) : 15;
|
||||
$endMoment = $moment->modify('+' . $durationMinutes . ' minutes');
|
||||
}
|
||||
|
||||
$endUtc = $endMoment->setTimezone(new DateTimeZone('UTC'));
|
||||
$uidBase = sha1($startUtc->format(DateTimeInterface::ATOM) . '|' . $summary . '|' . (string) $index);
|
||||
|
||||
$eventLines = [
|
||||
'BEGIN:VEVENT',
|
||||
'UID:' . $uidBase . '@astrotools.local',
|
||||
'DTSTAMP:' . $createdUtc,
|
||||
'DTSTART:' . $startUtc->format('Ymd\THis\Z'),
|
||||
'DTEND:' . $endUtc->format('Ymd\THis\Z'),
|
||||
'SUMMARY:' . escapeIcalText($summary),
|
||||
'DESCRIPTION:' . escapeIcalText($summary),
|
||||
'END:VEVENT',
|
||||
];
|
||||
|
||||
foreach ($eventLines as $eventLine) {
|
||||
$lines[] = foldIcalLine($eventLine);
|
||||
}
|
||||
}
|
||||
|
||||
$lines[] = 'END:VCALENDAR';
|
||||
|
||||
return implode("\r\n", $lines) . "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$dbConfig = require __DIR__ . '/../config/database.php';
|
||||
$dsn = sprintf(
|
||||
@@ -766,6 +997,49 @@ if (is_array($mercuryGoodVisibilityDecoded) && ($mercuryGoodVisibilityDecoded['o
|
||||
$mercuryGoodVisibilityEvents = is_array($mercuryGoodVisibilityDecoded['events'] ?? null) ? $mercuryGoodVisibilityDecoded['events'] : [];
|
||||
}
|
||||
|
||||
$planetParadeOutput = [];
|
||||
$planetParadeExitCode = 0;
|
||||
$planetParadeArgs = [
|
||||
$pythonScriptPath,
|
||||
'planet_parades_for_month',
|
||||
(string) $chartLatitude,
|
||||
(string) $chartLongitude,
|
||||
(string) $chartElevation,
|
||||
(string) $selectedYear,
|
||||
(string) $selectedMonth,
|
||||
$chartTimezone,
|
||||
(string) $planetParadeSampleOffsetMinutes,
|
||||
(string) $planetParadeMinAltitudeDeg,
|
||||
(string) $planetParadeMaxSunAltitudeDeg,
|
||||
(string) $planetParadeMaxAzimuthSpanDeg,
|
||||
(string) $planetParadeMinPlanetCount,
|
||||
(string) $planetParadeMinConsecutiveDays,
|
||||
];
|
||||
$planetParadeCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $planetParadeArgs)) . ' 2>&1';
|
||||
exec($planetParadeCommand, $planetParadeOutput, $planetParadeExitCode);
|
||||
$planetParadeRaw = trim(implode("\n", $planetParadeOutput));
|
||||
$planetParadeDecoded = json_decode($planetParadeRaw, true);
|
||||
if (is_array($planetParadeDecoded) && ($planetParadeDecoded['ok'] ?? false)) {
|
||||
$planetParadeEvents = is_array($planetParadeDecoded['events'] ?? null) ? $planetParadeDecoded['events'] : [];
|
||||
}
|
||||
|
||||
$planetConstellationChangeOutput = [];
|
||||
$planetConstellationChangeExitCode = 0;
|
||||
$planetConstellationChangeArgs = [
|
||||
$pythonScriptPath,
|
||||
'planet_constellation_changes_for_month',
|
||||
(string) $selectedYear,
|
||||
(string) $selectedMonth,
|
||||
$chartTimezone,
|
||||
];
|
||||
$planetConstellationChangeCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $planetConstellationChangeArgs)) . ' 2>&1';
|
||||
exec($planetConstellationChangeCommand, $planetConstellationChangeOutput, $planetConstellationChangeExitCode);
|
||||
$planetConstellationChangeRaw = trim(implode("\n", $planetConstellationChangeOutput));
|
||||
$planetConstellationChangeDecoded = json_decode($planetConstellationChangeRaw, true);
|
||||
if (is_array($planetConstellationChangeDecoded) && ($planetConstellationChangeDecoded['ok'] ?? false)) {
|
||||
$planetConstellationChangeEvents = is_array($planetConstellationChangeDecoded['events'] ?? null) ? $planetConstellationChangeDecoded['events'] : [];
|
||||
}
|
||||
|
||||
$moonOccultationStars = [];
|
||||
try {
|
||||
if (isset($pdo)) {
|
||||
@@ -1054,6 +1328,8 @@ foreach ($jupiterMoonSideEvents as $jupiterMoonSideEvent) {
|
||||
'date' => (string) ($jupiterMoonSideEvent['local_date'] ?? ''),
|
||||
'time' => (string) ($jupiterMoonSideEvent['local_time'] ?? ''),
|
||||
'local_iso' => (string) ($jupiterMoonSideEvent['local_iso'] ?? ''),
|
||||
'end_local_iso' => (string) ($jupiterMoonSideEvent['end_local_iso'] ?? ''),
|
||||
'duration_minutes' => isset($jupiterMoonSideEvent['duration_minutes']) ? (int) $jupiterMoonSideEvent['duration_minutes'] : null,
|
||||
'type' => 'jupiter_moons_one_side',
|
||||
];
|
||||
}
|
||||
@@ -1140,10 +1416,42 @@ foreach ($mercuryGoodVisibilityEvents as $visibilityEvent) {
|
||||
'date' => (string) ($visibilityEvent['local_date'] ?? ''),
|
||||
'time' => (string) ($visibilityEvent['local_time'] ?? ''),
|
||||
'local_iso' => (string) ($visibilityEvent['local_iso'] ?? ''),
|
||||
'end_local_iso' => (string) ($visibilityEvent['end_local_iso'] ?? ''),
|
||||
'duration_minutes' => isset($visibilityEvent['duration_minutes']) ? (int) $visibilityEvent['duration_minutes'] : null,
|
||||
'type' => 'mercury_good_visibility',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($planetParadeEvents as $paradeEvent) {
|
||||
if (!is_array($paradeEvent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$monthEventList[] = [
|
||||
'event' => (string) ($paradeEvent['label'] ?? 'Planetenparade'),
|
||||
'date' => (string) ($paradeEvent['local_date'] ?? ''),
|
||||
'time' => (string) ($paradeEvent['local_time'] ?? ''),
|
||||
'local_iso' => (string) ($paradeEvent['local_iso'] ?? ''),
|
||||
'end_local_iso' => (string) ($paradeEvent['end_local_iso'] ?? ''),
|
||||
'duration_minutes' => isset($paradeEvent['duration_minutes']) ? (int) $paradeEvent['duration_minutes'] : null,
|
||||
'type' => 'planet_parade',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($planetConstellationChangeEvents as $changeEvent) {
|
||||
if (!is_array($changeEvent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$monthEventList[] = [
|
||||
'event' => (string) ($changeEvent['label'] ?? 'Planet wechselt Sternbild'),
|
||||
'date' => (string) ($changeEvent['local_date'] ?? ''),
|
||||
'time' => (string) ($changeEvent['local_time'] ?? ''),
|
||||
'local_iso' => (string) ($changeEvent['local_iso'] ?? ''),
|
||||
'type' => 'planet_constellation_change',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($moonOccultations as $occultationEvent) {
|
||||
if (!is_array($occultationEvent)) {
|
||||
continue;
|
||||
@@ -1227,6 +1535,9 @@ usort(
|
||||
static fn (array $a, array $b): int => strcmp((string) ($a['local_iso'] ?? ''), (string) ($b['local_iso'] ?? ''))
|
||||
);
|
||||
|
||||
$observationTipInsertSql = buildObservationTipInsertSql($monthEventList, new DateTimeZone($chartTimezone));
|
||||
$observationTipCalendarIcs = buildObservationTipCalendarIcs($monthEventList, new DateTimeZone($chartTimezone));
|
||||
|
||||
$chartConfig = [
|
||||
'monthName' => $months[$selectedMonth],
|
||||
'year' => $selectedYear,
|
||||
@@ -1256,6 +1567,8 @@ $planetDataJson = json_encode($planetData, JSON_UNESCAPED_UNICODE | JSON_UNESCAP
|
||||
$moonDataJson = json_encode($moonData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$moonPhaseEventsJson = json_encode($moonPhaseEvents, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$observationTipCalendarIcsJson = json_encode($observationTipCalendarIcs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$observationTipCalendarFilenameJson = json_encode(sprintf('astrotools-monatsereignisse-%04d-%02d.ics', $selectedYear, $selectedMonth), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
|
||||
<section class="card monatsvorhersage-card">
|
||||
@@ -1358,9 +1671,30 @@ $starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_U
|
||||
<h3>Ereignisse <?= htmlspecialchars($months[$selectedMonth] . ' ' . (string) $selectedYear, ENT_QUOTES, 'UTF-8') ?></h3>
|
||||
<p>Chronologische Monatsliste fuer <?= htmlspecialchars($chartTimezone, ENT_QUOTES, 'UTF-8') ?></p>
|
||||
</div>
|
||||
<div class="monatskarte-export">
|
||||
<?php if ($observationTipInsertSql !== ''): ?>
|
||||
<button type="button" class="monatskarte-export-button" id="toggleObservationTipSql">
|
||||
SQL-Insert anzeigen
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($observationTipCalendarIcs !== ''): ?>
|
||||
<button type="button" class="monatskarte-export-button" id="downloadObservationTipIcal">
|
||||
iCal herunterladen
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="monatskarte-footer">
|
||||
<?php if ($observationTipInsertSql !== ''): ?>
|
||||
<div class="monatskarte-sql-export" id="observationTipSqlPanel" hidden>
|
||||
<label class="monatskarte-sql-label" for="observationTipSqlOutput">SQL fuer `astro_sonobs_observationtips`</label>
|
||||
<textarea id="observationTipSqlOutput" readonly><?= htmlspecialchars($observationTipInsertSql, ENT_QUOTES, 'UTF-8') ?></textarea>
|
||||
<div class="monatskarte-sql-actions">
|
||||
<button type="button" class="monatskarte-export-button" id="copyObservationTipSql">SQL kopieren</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($monthEventList): ?>
|
||||
<div class="riseset-grid" style="grid-template-columns: 1.5fr 1fr 1fr; row-gap: 10px;">
|
||||
<span class="riseset-label riseset-dim">Ereignis</span>
|
||||
@@ -1478,6 +1812,54 @@ $starDataErrorJson = json_encode($starDataError, JSON_UNESCAPED_UNICODE | JSON_U
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.monatskarte-export {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monatskarte-export-button {
|
||||
min-height: 40px;
|
||||
padding: 8px 14px;
|
||||
border: 1px solid rgba(150, 190, 255, 0.72);
|
||||
border-radius: 999px;
|
||||
background: rgba(24, 43, 83, 0.92);
|
||||
color: rgba(245, 248, 255, 0.96);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monatskarte-sql-export {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-bottom: 18px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(108, 145, 220, 0.22);
|
||||
border-radius: 16px;
|
||||
background: rgba(6, 11, 22, 0.72);
|
||||
}
|
||||
|
||||
.monatskarte-sql-label {
|
||||
color: rgba(235, 242, 255, 0.95);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.monatskarte-sql-export textarea {
|
||||
width: 100%;
|
||||
min-height: 220px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid rgba(108, 145, 220, 0.28);
|
||||
border-radius: 12px;
|
||||
background: rgba(3, 7, 14, 0.96);
|
||||
color: rgba(233, 239, 250, 0.94);
|
||||
font: 13px/1.5 Consolas, Monaco, monospace;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.monatskarte-sql-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.monatskarte-stage {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
@@ -1546,6 +1928,62 @@ const MONTH_CHART_PLANETS = <?= $planetDataJson ?: '[]' ?>;
|
||||
const MONTH_CHART_MOON = <?= $moonDataJson ?: 'null' ?>;
|
||||
const MONTH_PHASE_EVENTS = <?= $moonPhaseEventsJson ?: '[]' ?>;
|
||||
const MONTH_CHART_ERROR = <?= $starDataErrorJson ?: 'null' ?>;
|
||||
const OBSERVATION_TIP_ICAL = <?= $observationTipCalendarIcsJson ?: '""' ?>;
|
||||
const OBSERVATION_TIP_ICAL_FILENAME = <?= $observationTipCalendarFilenameJson ?: '"astrotools-monatsereignisse.ics"' ?>;
|
||||
|
||||
(function () {
|
||||
const toggleButton = document.getElementById('toggleObservationTipSql');
|
||||
const copyButton = document.getElementById('copyObservationTipSql');
|
||||
const icalButton = document.getElementById('downloadObservationTipIcal');
|
||||
const panel = document.getElementById('observationTipSqlPanel');
|
||||
const output = document.getElementById('observationTipSqlOutput');
|
||||
|
||||
if (toggleButton && panel) {
|
||||
toggleButton.addEventListener('click', function () {
|
||||
const isHidden = panel.hasAttribute('hidden');
|
||||
if (isHidden) {
|
||||
panel.removeAttribute('hidden');
|
||||
toggleButton.textContent = 'SQL-Insert ausblenden';
|
||||
} else {
|
||||
panel.setAttribute('hidden', '');
|
||||
toggleButton.textContent = 'SQL-Insert anzeigen';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (copyButton && output && navigator.clipboard) {
|
||||
copyButton.addEventListener('click', async function () {
|
||||
try {
|
||||
await navigator.clipboard.writeText(output.value);
|
||||
copyButton.textContent = 'SQL kopiert';
|
||||
window.setTimeout(function () {
|
||||
copyButton.textContent = 'SQL kopieren';
|
||||
}, 1800);
|
||||
} catch (error) {
|
||||
copyButton.textContent = 'Kopieren fehlgeschlagen';
|
||||
window.setTimeout(function () {
|
||||
copyButton.textContent = 'SQL kopieren';
|
||||
}, 1800);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (icalButton && OBSERVATION_TIP_ICAL) {
|
||||
icalButton.addEventListener('click', function () {
|
||||
const blob = new Blob([OBSERVATION_TIP_ICAL], { type: 'text/calendar;charset=utf-8' });
|
||||
const objectUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = objectUrl;
|
||||
link.download = OBSERVATION_TIP_ICAL_FILENAME || 'astrotools-monatsereignisse.ics';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.setTimeout(function () {
|
||||
window.URL.revokeObjectURL(objectUrl);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const imageEl = document.getElementById('monatsphasenImage');
|
||||
|
||||
Reference in New Issue
Block a user