Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Moon Flash - Lunar Impact Visualization</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #0a0a1a 0%, #1a1a3a 100%); | |
| color: #fff; | |
| overflow: hidden; | |
| height: 100vh; | |
| height: 100dvh; | |
| overscroll-behavior: none; | |
| } | |
| #container { | |
| position: relative; | |
| width: 100%; | |
| height: 100%; | |
| height: 100dvh; | |
| } | |
| #canvas { | |
| display: block; | |
| width: 100%; | |
| height: 100%; | |
| touch-action: none; | |
| } | |
| #info { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 20px; | |
| border-radius: 10px; | |
| backdrop-filter: blur(10px); | |
| max-width: 300px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| #info-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 12px; | |
| } | |
| #info-collapse { | |
| width: 34px; | |
| height: 30px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| background: rgba(255, 255, 255, 0.08); | |
| color: #fff; | |
| cursor: pointer; | |
| line-height: 1; | |
| font-size: 1.1rem; | |
| } | |
| #info-collapse:hover { | |
| background: rgba(255, 255, 255, 0.14); | |
| } | |
| #info-body { | |
| margin-top: 6px; | |
| } | |
| #info.collapsed { | |
| padding: 14px; | |
| max-width: 220px; | |
| } | |
| #info.collapsed #info-body { | |
| display: none; | |
| } | |
| #info h1 { | |
| font-size: 1.5rem; | |
| margin-bottom: 10px; | |
| color: #ffcc00; | |
| } | |
| #info-header h1 { | |
| margin-bottom: 0; | |
| } | |
| #info p { | |
| font-size: 0.9rem; | |
| margin-bottom: 5px; | |
| line-height: 1.5; | |
| } | |
| #grid-toggle { | |
| margin-top: 10px; | |
| width: 100%; | |
| padding: 8px 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| background: rgba(255, 255, 255, 0.08); | |
| color: #fff; | |
| cursor: pointer; | |
| } | |
| #grid-toggle:hover { | |
| background: rgba(255, 255, 255, 0.14); | |
| } | |
| #impact-anim-toggle { | |
| margin-top: 8px; | |
| width: 100%; | |
| padding: 8px 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| background: rgba(255, 255, 255, 0.08); | |
| color: #fff; | |
| cursor: pointer; | |
| } | |
| #impact-anim-toggle:hover { | |
| background: rgba(255, 255, 255, 0.14); | |
| } | |
| #help-toggle { | |
| margin-top: 8px; | |
| width: 100%; | |
| padding: 8px 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| background: rgba(255, 255, 255, 0.08); | |
| color: #fff; | |
| cursor: pointer; | |
| } | |
| #help-toggle:hover { | |
| background: rgba(255, 255, 255, 0.14); | |
| } | |
| #details { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 20px; | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 20px; | |
| border-radius: 10px; | |
| backdrop-filter: blur(10px); | |
| max-width: 350px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| display: none; | |
| opacity: 0; | |
| transform: translateY(14px); | |
| transition: opacity 180ms ease, transform 180ms ease; | |
| pointer-events: none; | |
| } | |
| #details.visible { | |
| opacity: 1; | |
| transform: translateY(0); | |
| pointer-events: auto; | |
| } | |
| #details.bump { | |
| animation: details-bump 180ms ease; | |
| } | |
| @keyframes details-bump { | |
| 0% { transform: translateY(0) scale(1); } | |
| 45% { transform: translateY(-2px) scale(1.02); } | |
| 100% { transform: translateY(0) scale(1); } | |
| } | |
| #details h2 { | |
| font-size: 1.2rem; | |
| margin-bottom: 10px; | |
| color: #ff6b6b; | |
| } | |
| #details p { | |
| font-size: 0.9rem; | |
| margin-bottom: 5px; | |
| } | |
| #close-details { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| } | |
| #close-details:hover { | |
| color: #ff6b6b; | |
| } | |
| #coord-tooltip { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| transform: translate(-50%, -120%); | |
| background: rgba(0, 0, 0, 0.75); | |
| border: 1px solid rgba(255, 255, 255, 0.12); | |
| border-radius: 8px; | |
| padding: 8px 10px; | |
| font-size: 0.85rem; | |
| pointer-events: none; | |
| white-space: nowrap; | |
| display: none; | |
| } | |
| #help { | |
| position: absolute; | |
| inset: 0; | |
| background: rgba(0, 0, 0, 0.65); | |
| display: none; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| } | |
| #help-content { | |
| position: relative; | |
| width: min(760px, 95vw); | |
| max-height: min(78vh, 720px); | |
| overflow: auto; | |
| background: rgba(0, 0, 0, 0.85); | |
| border: 1px solid rgba(255, 255, 255, 0.12); | |
| border-radius: 12px; | |
| padding: 18px 20px; | |
| backdrop-filter: blur(10px); | |
| } | |
| #help-content h2 { | |
| font-size: 1.2rem; | |
| margin-bottom: 10px; | |
| color: #ffcc00; | |
| } | |
| #help-content p { | |
| font-size: 0.92rem; | |
| margin-bottom: 10px; | |
| line-height: 1.55; | |
| color: rgba(255, 255, 255, 0.92); | |
| } | |
| #help-content ul { | |
| margin: 0 0 10px 18px; | |
| padding: 0; | |
| line-height: 1.55; | |
| color: rgba(255, 255, 255, 0.92); | |
| } | |
| #help-close { | |
| position: absolute; | |
| top: 10px; | |
| right: 12px; | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| } | |
| #help-close:hover { | |
| color: #ff6b6b; | |
| } | |
| #loading { | |
| position: absolute; | |
| inset: 0; | |
| display: none; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(0, 0, 0, 0.6); | |
| backdrop-filter: blur(10px); | |
| z-index: 50; | |
| } | |
| #loading-content { | |
| width: min(520px, 92vw); | |
| background: rgba(0, 0, 0, 0.75); | |
| border: 1px solid rgba(255, 255, 255, 0.12); | |
| border-radius: 12px; | |
| padding: 16px 18px; | |
| } | |
| #loading-title { | |
| font-size: 1.05rem; | |
| color: #ffcc00; | |
| margin-bottom: 10px; | |
| } | |
| #loading-text { | |
| font-size: 0.92rem; | |
| color: rgba(255, 255, 255, 0.9); | |
| margin-bottom: 12px; | |
| line-height: 1.45; | |
| } | |
| #loading-bar-wrap { | |
| height: 10px; | |
| background: rgba(255, 255, 255, 0.10); | |
| border: 1px solid rgba(255, 255, 255, 0.10); | |
| border-radius: 999px; | |
| overflow: hidden; | |
| } | |
| #loading-bar { | |
| height: 100%; | |
| width: 0%; | |
| background: linear-gradient(90deg, rgba(255, 204, 0, 0.35), rgba(255, 204, 0, 0.95)); | |
| } | |
| #loading-bar.indeterminate { | |
| animation: loading-indeterminate 900ms ease-in-out infinite; | |
| } | |
| @keyframes loading-indeterminate { | |
| 0% { transform: translateX(-60%); opacity: 0.7; } | |
| 50% { transform: translateX(40%); opacity: 1; } | |
| 100% { transform: translateX(140%); opacity: 0.7; } | |
| } | |
| @media (max-width: 640px) { | |
| #info { | |
| top: 10px; | |
| left: 10px; | |
| padding: 12px; | |
| max-width: calc(100vw - 20px); | |
| } | |
| #info h1 { | |
| font-size: 1.25rem; | |
| } | |
| #details { | |
| left: 10px; | |
| right: 10px; | |
| bottom: 10px; | |
| max-width: none; | |
| max-height: min(44vh, 380px); | |
| overflow: auto; | |
| } | |
| #help-content { | |
| width: calc(100vw - 24px); | |
| max-height: 82vh; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="container"> | |
| <canvas id="canvas"></canvas> | |
| <div id="loading" aria-live="polite" aria-label="Loading"> | |
| <div id="loading-content"> | |
| <div id="loading-title">Loading Moon Model</div> | |
| <div id="loading-text">Starting…</div> | |
| <div id="loading-bar-wrap"><div id="loading-bar"></div></div> | |
| </div> | |
| </div> | |
| <div id="coord-tooltip"></div> | |
| <div id="info"> | |
| <div id="info-header"> | |
| <h1>Moon Flash</h1> | |
| <button id="info-collapse" type="button">−</button> | |
| </div> | |
| <div id="info-body"> | |
| <p>Visualizing lunar impact events</p> | |
| <p>Hover over markers for explosions</p> | |
| <p>Click on impact markers for details</p> | |
| <p>Drag to rotate, scroll to zoom</p> | |
| <button id="grid-toggle" type="button">Toggle Lat/Lon Grid</button> | |
| <button id="impact-anim-toggle" type="button">Hide Overlays</button> | |
| <button id="help-toggle" type="button">Help</button> | |
| </div> | |
| </div> | |
| <div id="details"> | |
| <button id="close-details">×</button> | |
| <h2 id="details-title">Impact Details</h2> | |
| <p id="details-csv-id"></p> | |
| <p id="details-date"></p> | |
| <p id="details-time"></p> | |
| <p id="details-coords"></p> | |
| <p id="details-duration"></p> | |
| <p id="details-observer"></p> | |
| <p id="details-magnitude"></p> | |
| <p id="details-airmass"></p> | |
| <p id="details-altitude"></p> | |
| <p id="details-azimuth"></p> | |
| <p id="details-peak"></p> | |
| <p id="details-classification"></p> | |
| </div> | |
| <div id="help" role="dialog" aria-modal="true" aria-label="Help"> | |
| <div id="help-content"> | |
| <button id="help-close" type="button">×</button> | |
| <h2>Help</h2> | |
| <p>This program visualizes lunar impact flash events on a 3D Moon. Hover an impact to play an animation, and click an impact to see its details.</p> | |
| <p>Data source:</p> | |
| <ul> | |
| <li>Event data comes from NELIOTA (National Observatory of Athens): <b>https://neliota.astro.noa.gr/DataAccess</b> (Event Details pages).</li> | |
| <li>In this project, events are stored locally under <b>data/event-<id>/</b>.</li> | |
| <li>The viewer reads <b>event-<id>.csv</b> (generated from the downloaded HTML pages) to populate the details panel.</li> | |
| </ul> | |
| <p>Verification:</p> | |
| <ul> | |
| <li>Cross-check event coordinates and visibility with <b>https://hdevillepoix.github.io/LIF_Aladin/</b>.</li> | |
| </ul> | |
| <p>Impact marker meaning:</p> | |
| <ul> | |
| <li><b>Color</b> is based on the average of <b>R (mag)</b> and <b>I (mag)</b>: low average magnitude is yellow, high average magnitude is red.</li> | |
| <li><b>Size</b> is based on <b>Duration (sec)</b>: longer duration events render larger markers.</li> | |
| </ul> | |
| <p>Credit: the program is created by Andy Kong</p> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js", | |
| "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/" | |
| } | |
| } | |
| </script> | |
| <script type="module" src="app.js?v=20260527"></script> | |
| </body> | |
| </html> | |