Download index.html from Major-TOM/MajorTOM-Raster-Viewer: direct link, hf CLI and curl.
- Browser
- Download file 18.2 kB
-
https://huggingface.co/spaces/Major-TOM/MajorTOM-Raster-Viewer/resolve/main/index.html
- Command line
-
hf download hf://spaces/Major-TOM/MajorTOM-Raster-Viewer/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Major-TOM/MajorTOM-Raster-Viewer/resolve/main/index.html
18.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Major TOM Raster Viewer</title> | |
| <link rel="icon" href="mt-icon.svg"> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=League+Spartan:wght@500&family=Source+Sans+3:wght@400;600;700&display=swap"> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.14.0/dist/maplibre-gl.css"> | |
| <style> | |
| :root { | |
| /* Major TOM logo colours */ | |
| --mt-blue: #0071bc; --mt-navy: #2e3192; --mt-green: #39b54a; --mt-orange: #f15a24; | |
| /* light interface, as Hugging Face in light mode */ | |
| --page: #f9fafb; --surface: #ffffff; --ink: #111827; --ink-2: #4b5563; --ink-3: #6b7280; | |
| --line: #e5e7eb; --tint: #eff6fc; --globe-space: #f3f4f6; | |
| --sans: 'Source Sans 3', system-ui, sans-serif; --mono: 'IBM Plex Mono', ui-monospace, monospace; | |
| } | |
| * { box-sizing: border-box; } | |
| html, body { height: 100%; margin: 0; } | |
| body { background: var(--page); color: var(--ink); font: 15px/1.45 var(--sans); display: flex; flex-direction: column; } | |
| header { background: var(--surface); border-bottom: 1px solid var(--line); position: relative; } | |
| .brand-strip { display: flex; height: 3px; } | |
| .brand-strip span { flex: 1; } | |
| .bar { display: flex; align-items: center; gap: 12px; padding: 10px 20px; } | |
| .bar img { width: 30px; height: 26px; } | |
| .crumbs { font-family: var(--mono); font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .crumbs .org { color: var(--ink-3); } | |
| .crumbs b { font-weight: 500; } | |
| .pill { font-size: 12px; color: var(--ink-2); border: 1px solid var(--line); border-radius: 999px; padding: 1px 9px; white-space: nowrap; } | |
| #loader { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 3; display: flex; | |
| flex-direction: column; align-items: center; gap: 6px; min-width: 220px; padding: 18px 22px 14px; | |
| background: rgba(255,255,255,.94); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 4px 16px rgba(17,24,39,.08); } | |
| #loader[hidden] { display: none; } | |
| #loader svg { width: 64px; height: 56px; margin-bottom: 4px; } | |
| #loader polygon { fill: var(--line); transition: fill .12s; } | |
| #loader polygon.on { fill: var(--on); } | |
| #loader-phase { font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); } | |
| #loader-text { font-size: 14px; color: var(--ink); text-align: center; } | |
| #loader-bytes { font-family: var(--mono); font-size: 12px; color: var(--ink-3); min-height: 1em; } | |
| .bar nav { margin-left: auto; display: flex; gap: 16px; font-size: 14px; } | |
| .bar nav a { color: var(--ink-2); text-decoration: none; white-space: nowrap; } | |
| .bar nav a:hover { color: var(--mt-blue); } | |
| main { flex: 1; display: flex; min-height: 0; } | |
| #map-wrap { flex: 1; position: relative; background: var(--globe-space); } | |
| #map { position: absolute; inset: 0; } | |
| .places { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 1; } | |
| .place { position: absolute; left: 0; top: 0; margin: -7px 0 0 -4px; display: flex; align-items: center; gap: 4px; | |
| font: 600 12px/14px var(--sans); color: var(--ink); white-space: nowrap; | |
| text-shadow: 0 0 2px #fff, 0 0 3px #fff, 0 0 5px rgba(255,255,255,.9); } | |
| .place i { font-style: normal; font-size: 8px; color: var(--ink-2); } | |
| .place.capital span { font-weight: 700; } | |
| .place.peak i, .place.low i, .place.pass i { color: #6b4f2a; } | |
| .place.peak span, .place.low span, .place.pass span, .place.falls span { font-weight: 400; font-style: italic; } | |
| .toggle { display: flex; gap: 8px; align-items: center; font-size: 14px; cursor: pointer; } | |
| .toggle input { accent-color: var(--mt-blue); } | |
| #status:not(:empty) { position: absolute; top: 14px; left: 14px; z-index: 2; background: var(--surface); | |
| border: 1px solid var(--line); border-radius: 8px; padding: 4px 10px; font-size: 13px; color: var(--ink-2); } | |
| aside { width: 344px; flex: none; background: var(--surface); border-left: 1px solid var(--line); overflow-y: auto; } | |
| section { padding: 14px 18px 16px; border-bottom: 1px solid var(--line); } | |
| h2 { font-size: 13px; font-weight: 600; color: var(--ink-3); margin: 0 0 10px; } | |
| .option { display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px; border: 1px solid var(--line); | |
| border-radius: 8px; margin-bottom: 6px; cursor: pointer; } | |
| .option:has(input:checked) { border-color: var(--mt-blue); background: var(--tint); } | |
| .option input { margin: 4px 0 0; accent-color: var(--mt-blue); } | |
| .option b { display: block; font-weight: 600; } | |
| .option small { display: block; color: var(--ink-3); font-family: var(--mono); font-size: 12px; } | |
| .note { font-size: 13px; color: var(--ink-3); margin: 0; } | |
| .pick-head { display: flex; justify-content: space-between; align-items: baseline; } | |
| .pick-head h2 { margin-bottom: 4px; } | |
| .link { font: inherit; font-size: 13px; color: var(--mt-blue); background: none; border: 0; padding: 0; cursor: pointer; } | |
| .pick-name { display: flex; justify-content: space-between; gap: 8px; font-family: var(--mono); font-size: 13px; margin-bottom: 10px; } | |
| .pick-name b { font-weight: 500; } | |
| .pick-name span { color: var(--ink-3); } | |
| #thumb-wrap { position: relative; aspect-ratio: 1; background: var(--globe-space); border-radius: 8px; overflow: hidden; | |
| margin-bottom: 10px; display: flex; align-items: center; justify-content: center; color: var(--ink-3); font-size: 13px; } | |
| #thumb { width: 100%; height: 100%; object-fit: contain; display: block; } | |
| #thumb[hidden] { display: none; } | |
| #thumb { cursor: zoom-in; } | |
| #thumb-badge { position: absolute; left: 8px; bottom: 8px; display: flex; align-items: center; gap: 7px; padding: 4px 10px 4px 8px; | |
| border-radius: 999px; background: rgba(17, 24, 39, .78); color: #fff; font: 12px var(--sans); pointer-events: none; } | |
| #thumb-badge[hidden] { display: none; } | |
| #thumb-badge .spin { width: 11px; height: 11px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, .35); | |
| border-top-color: #fff; animation: spin .8s linear infinite; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .image-actions { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin: -2px 0 10px; font-size: 13px; } | |
| .image-actions button { font: inherit; padding: 4px 10px; border: 1px solid var(--line); border-radius: 8px; | |
| background: var(--surface); color: var(--ink); cursor: pointer; } | |
| .image-actions button:hover { border-color: var(--mt-blue); color: var(--mt-blue); } | |
| .image-actions button:disabled { opacity: .45; cursor: not-allowed; border-color: var(--line); color: var(--ink); } | |
| .image-actions label { display: flex; align-items: center; gap: 6px; color: var(--ink-2); cursor: pointer; } | |
| .image-actions input[type=checkbox], .image-actions input[type=range] { accent-color: var(--mt-blue); } | |
| .image-actions input[type=range] { width: 90px; } | |
| .image-actions[hidden] { display: none; } | |
| .view-row { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; font-size: 13px; color: var(--ink-2); } | |
| .view-row select { flex: 1; padding: 5px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); | |
| color: var(--ink); font: 13px var(--sans); } | |
| .view-row[hidden] { display: none; } | |
| #lightbox { position: fixed; inset: 0; z-index: 10; background: rgba(17,24,39,.88); overflow: hidden; cursor: grab; | |
| touch-action: none; } | |
| #lightbox[hidden] { display: none; } | |
| #lightbox.dragging { cursor: grabbing; } | |
| #lightbox img { position: absolute; left: 0; top: 0; transform-origin: 0 0; image-rendering: pixelated; user-select: none; | |
| -webkit-user-drag: none; } | |
| #lightbox-bar { position: absolute; left: 0; right: 0; top: 0; display: flex; justify-content: space-between; gap: 12px; | |
| align-items: center; padding: 10px 16px; color: #fff; font: 13px var(--mono); background: rgba(17,24,39,.6); } | |
| #lightbox-bar button { font: 13px var(--sans); padding: 4px 12px; border: 1px solid rgba(255,255,255,.4); border-radius: 8px; | |
| background: transparent; color: #fff; cursor: pointer; } | |
| #choices { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; } | |
| #choices:empty { display: none; } | |
| #choices button { font: 12px var(--mono); padding: 3px 8px; border: 1px solid var(--line); border-radius: 999px; | |
| background: var(--surface); color: var(--ink-2); cursor: pointer; } | |
| #choices button[aria-pressed=true] { border-color: var(--mt-blue); color: var(--mt-blue); background: var(--tint); } | |
| #weather { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); } | |
| #weather h3 { margin: 0 0 2px; font-size: 15px; font-weight: 600; } | |
| .wx-head { font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); margin: 14px 0 4px; } | |
| .wx-row { margin: 8px 0 0; } | |
| .wx-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; } | |
| .wx-label { font-size: 14px; color: var(--ink); } | |
| .wx-label small { font-size: 12px; color: var(--ink-3); margin-left: 2px; } | |
| .wx-value { font: 500 14px var(--mono); white-space: nowrap; } | |
| .wx-value small { font-size: 11px; color: var(--ink-3); font-weight: 400; } | |
| .wx-chart { display: block; width: 100%; height: auto; margin-top: 2px; overflow: visible; } | |
| .wx-line { fill: none; stroke: var(--mt-blue); stroke-width: 1.6; stroke-linejoin: round; } | |
| .wx-area { fill: var(--mt-blue); opacity: .08; } | |
| .wx-bars rect { fill: var(--mt-blue); } | |
| .wx-dots circle { fill: var(--mt-blue); } | |
| .wx-base { stroke: var(--line); } | |
| .wx-zero { stroke: var(--mt-orange); stroke-width: 1; stroke-dasharray: 2 2; } | |
| .wx-now { fill: var(--mt-orange); stroke: var(--surface); stroke-width: 1.5; } | |
| .wx-tick { font: 10px var(--mono); fill: var(--ink-3); text-anchor: end; } | |
| .wx-none { font: 11px var(--sans); fill: var(--ink-3); text-anchor: middle; } | |
| .wx-axis { display: flex; justify-content: space-between; font: 10px var(--mono); color: var(--ink-3); margin: 4px 40px 0 0; padding-top: 3px; border-top: 1px solid var(--line); } | |
| .tiny { font: 11px/1.4 var(--mono); color: var(--ink-3); min-height: 1em; margin-top: 8px; } | |
| dl { display: grid; grid-template-columns: auto 1fr; gap: 3px 14px; margin: 0; font-size: 14px; } | |
| dt { color: var(--ink-3); } | |
| dd { margin: 0; font-family: var(--mono); font-size: 13px; text-align: right; overflow-wrap: anywhere; } | |
| dd a { color: var(--mt-blue); } | |
| .source { font-size: 12px; color: var(--ink-3); padding: 12px 18px 18px; } | |
| .credit { display: flex; flex-direction: column; gap: 8px; font-size: 12px; color: var(--ink-3); padding: 14px 18px 20px; | |
| border-top: 1px solid var(--line); } | |
| .credit p { margin: 0; } | |
| .credit a { color: var(--ink-2); } | |
| #asterisk { position: absolute; left: 14px; bottom: 14px; z-index: 2; display: flex; align-items: center; gap: 6px; | |
| font: 500 17px/1 'League Spartan', sans-serif; color: #000; text-decoration: none; } | |
| #asterisk img { height: 24px; } /* text at 0.7 x the mark, as on asterisk.coop */ | |
| #asterisk .by { font: 12px var(--sans); color: var(--ink-3); margin-right: 2px; } | |
| #asterisk:hover { color: #492ae8; } | |
| .source a { color: var(--mt-blue); } | |
| .maplibregl-ctrl-attrib { font-family: var(--sans); } | |
| @media (max-width: 820px) { | |
| main { flex-direction: column; overflow-y: auto; } | |
| #map-wrap { flex: none; height: 62vh; } | |
| aside { width: 100%; border-left: 0; border-top: 1px solid var(--line); overflow: visible; } | |
| .bar { padding: 10px 16px; } | |
| .bar nav, .pill { display: none; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="brand-strip" aria-hidden="true"> | |
| <span style="background:var(--mt-blue)"></span><span style="background:var(--mt-navy)"></span> | |
| <span style="background:var(--mt-green)"></span><span style="background:var(--mt-orange)"></span> | |
| </div> | |
| <div class="bar"> | |
| <img src="mt-icon.svg" alt=""> | |
| <div class="crumbs"><span class="org">Major-TOM /</span> <b>Raster Viewer</b></div> | |
| <span class="pill">serverless</span> | |
| <nav> | |
| <a href="https://huggingface.co/Major-TOM">Datasets</a> | |
| <a href="https://huggingface.co/spaces/Major-TOM/MajorTOM-Core-Viewer">Core Viewer</a> | |
| <a href="https://github.com/ESA-PhiLab/Major-TOM">Code</a> | |
| </nav> | |
| </div> | |
| </header> | |
| <main> | |
| <div id="map-wrap"> | |
| <div id="map"></div> | |
| <div id="places" class="places" aria-hidden="true"></div> | |
| <a id="asterisk" href="https://asterisk.coop" target="_blank" rel="noopener"> | |
| <span class="by">made by</span><img src="asterisk-mark.svg" alt="">asterisk labs</a> | |
| <div id="status"></div> | |
| <div id="loader" role="status" aria-live="polite" hidden> | |
| <!-- the Major TOM logo mark (mt-icon.svg): its four panels light up in turn while data loads --> | |
| <svg viewBox="0 0 345 296" aria-hidden="true"> | |
| <polygon style="--on:var(--mt-navy)" points="0 37 75 0 75 258 0 296"/> | |
| <polygon style="--on:var(--mt-blue)" points="75 0 172 37 172 296 75 258"/> | |
| <polygon style="--on:var(--mt-green)" points="172 37 269 0 269 258 172 296"/> | |
| <polygon style="--on:var(--mt-orange)" points="269 0 345 37 345 296 269 258"/> | |
| </svg> | |
| <div id="loader-phase"></div> | |
| <div id="loader-text"></div> | |
| <div id="loader-bytes"></div> | |
| </div> | |
| </div> | |
| <aside> | |
| <section> | |
| <h2>Dataset</h2> | |
| <div id="datasets"></div> | |
| <p class="note">Runs in your browser: reads the Core Parquet files on Hugging Face directly, no server. Click a | |
| cell with a sample to load its image.</p> | |
| </section> | |
| <section id="pick" hidden> | |
| <div class="pick-head"><h2>Selected cell</h2><button id="pick-clear" class="link">Clear</button></div> | |
| <div class="pick-name"><b id="pick-cell"></b><span id="pick-pos"></span></div> | |
| <div id="choices"></div> | |
| <label class="view-row" id="view-row" hidden>View <select id="view" aria-label="Band or composite"></select></label> | |
| <div id="thumb-wrap"><img id="thumb" alt="Thumbnail of the selected sample" hidden><span id="thumb-note"></span><div id="thumb-badge" hidden><span class="spin"></span><span id="thumb-badge-text"></span></div></div> | |
| <div class="image-actions" id="image-actions" hidden> | |
| <button id="open-image" title="The image in the full window: zoom, pan, value under the cursor">Enlarge</button> | |
| </div> | |
| <div class="image-actions" id="download-actions" hidden> | |
| <button id="dl-band" title="The shown band as stored: a GeoTIFF">Download band (.tif)</button> | |
| <button id="dl-all" title="Every band as stored, the thumbnail and metadata.json">All bands (.zip)</button> | |
| <button id="dl-python" title="Python that reads this sample from Hugging Face">Copy Python</button> | |
| </div> | |
| <dl id="pick-meta"></dl> | |
| <div class="tiny" id="pick-stats"></div> | |
| <div id="weather" hidden> | |
| <h3>Weather at acquisition</h3> | |
| <p class="note" id="wx-from" hidden></p> | |
| <div class="wx-head">Hourly · 24 hours either side</div><div id="wx-hourly"></div> | |
| <div class="wx-head">Daily · 30 days either side</div><div id="wx-daily"></div> | |
| <div class="tiny" id="wx-stats"></div> | |
| <p class="note">Means over the 10 km cell, from Core-Weather on source.coop. Orange: the acquisition.</p> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>Map</h2> | |
| <label class="toggle"><input type="checkbox" id="show-places" checked> Place names (cities, peaks) from zoom 5</label> | |
| </section> | |
| <section> | |
| <h2>Cursor</h2> | |
| <dl> | |
| <dt>Cell</dt><dd id="cursor-cell">–</dd> | |
| <dt>Latitude, longitude</dt><dd id="cursor-pos">–</dd> | |
| <dt>Sample</dt><dd id="cursor-value">–</dd> | |
| </dl> | |
| </section> | |
| <section> | |
| <h2>Last load</h2> | |
| <dl> | |
| <dt>Footprints</dt><dd id="stat-footprints">–</dd> | |
| <dt>Samples</dt><dd id="stat-samples">–</dd> | |
| <dt>Draw on globe</dt><dd id="stat-draw">–</dd> | |
| </dl> | |
| </section> | |
| <div class="source">Reads <a id="dataset-link" href="https://huggingface.co/Major-TOM">huggingface.co/Major-TOM</a> | |
| · footprints from each dataset's metadata.parquet</div> | |
| <div class="credit"> | |
| <a href="https://asterisk.coop/project-pages/earth-compress.html" target="_blank" rel="noopener"> | |
| <img src="earth-compress.svg" alt="Earth Compress" height="28"></a> | |
| <p>Developed by <a href="https://github.com/mikonvergence" target="_blank" rel="noopener">Mikolaj Czerkawski</a>, | |
| <a href="https://asterisk.coop" target="_blank" rel="noopener">Asterisk Labs</a>, as part of Earth Compress, an | |
| ARIA-funded project jointly led with the National Oceanography Centre.</p> | |
| </div> | |
| </aside> | |
| </main> | |
| <div id="lightbox" hidden role="dialog" aria-label="Selected image"> | |
| <img id="lightbox-img" alt=""> | |
| <div id="lightbox-bar"><span><span id="lightbox-title"></span> <span id="lightbox-value"></span></span><span>scroll to zoom · drag to pan · Esc to close | |
| <button id="lightbox-close">Close</button></span></div> | |
| </div> | |
| <script> | |
| // A private Space is shown in huggingface.co's frame through a signed link (?__sign=...), and only requests that | |
| // carry the signature are let through there: modules imported by other modules would not. So every file of the app | |
| // is asked for with it (an import map, then app.js). With no signature (public Space, local) nothing changes. | |
| { | |
| const sign = location.search, files = ['app.js', 'footprints.js', 'samples.js', 'bands.js', 'download.js', 'parquet.js', 'tiles.js', 'labels.js'] | |
| const url = file => new URL(file, location.href).href.split('?')[0] | |
| const map = document.createElement('script') | |
| map.type = 'importmap' | |
| map.textContent = JSON.stringify({ imports: Object.fromEntries(files.map(f => [url(f), url(f) + sign])) }) | |
| document.head.append(map) | |
| const app = document.createElement('script') | |
| app.type = 'module' | |
| app.src = 'app.js' + sign | |
| document.body.append(app) | |
| } | |
| </script> | |
| </body> | |
| </html> | |